From 76c8981cf5fb10240ba0780f8cdb70b37e229146 Mon Sep 17 00:00:00 2001 From: Robert Kirkman Date: Tue, 30 Jun 2026 18:39:34 -0500 Subject: [PATCH] If ashmem is not available, fall back to wrapping vanilla SYSV shared memory - Fixes https://github.com/termux/termux-docker/issues/87 - Fixes https://github.com/termux/libandroid-shmem/issues/13 - If vanilla SYSV shared memory is allowed to be run on normal Android, the program will crash, but this can be worked around by attempting to always detect whether ashmem is available before doing anything, and only wrapping vanilla SYSV shared memory in the case that ashmem is not available. If ashmem is not available AND SYSV shared memory is still blocked by SELinux, then on that device, logically there is no codepath that `libandroid-shmem` could have used to work, so the program could not have worked, and crashing in that case might be acceptable. - Is passing all of these tests on all devices of all architectures and all types of Android that have been tested so far: - `cd ~/.termux-build/libandroid-shmem/src/test && make` - `initdb $PREFIX/var/lib/postgresql` - `shmem-share.c` - `shmem-test.c` - Derived from code primarily originating from https://android.googlesource.com/platform/bionic/+/02ce401d1e2b31586c94c8b6999364bbbce27388/libc/bionic/sys_shm.cpp - Potential alternative to https://github.com/termux/libandroid-shmem/pull/21 --- .gitignore | 1 + Makefile | 8 +++---- shm_vanilla.h | 51 ++++++++++++++++++++++++++++++++++++++++ shmem.c | 32 ++++++++++++++++++++++++- shmem_vanilla.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ test/Makefile | 6 ++--- 6 files changed, 152 insertions(+), 8 deletions(-) create mode 100644 shm_vanilla.h create mode 100644 shmem_vanilla.c diff --git a/.gitignore b/.gitignore index 1930e81..ab6db66 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.so tags test/test-* +.vscode diff --git a/Makefile b/Makefile index 4a876ae..51d33ae 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,11 @@ CFLAGS += -fpic -shared -std=c11 -Wall -Wextra LDFLAGS += -Wl,--version-script=exports.txt -libandroid-shmem.a: shmem.o - $(AR) rcu $@ shmem.o +libandroid-shmem.a: shmem.o shmem_vanilla.o + $(AR) rcu $@ shmem.o shmem_vanilla.o -libandroid-shmem.so: shmem.o - $(CC) $(LDFLAGS) -shared shmem.o -o $@ -llog -landroid +libandroid-shmem.so: shmem.o shmem_vanilla.o + $(CC) $(LDFLAGS) -shared shmem.o shmem_vanilla.o -o $@ -llog -landroid shmem.o: shmem.c shm.h $(CC) $(CFLAGS) -c shmem.c -o $@ diff --git a/shm_vanilla.h b/shm_vanilla.h new file mode 100644 index 0000000..2c79fe9 --- /dev/null +++ b/shm_vanilla.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/** fork of https://android.googlesource.com/platform/bionic/+/02ce401d1e2b31586c94c8b6999364bbbce27388/libc/include/sys/shm.h */ + +#pragma once + +#include +#include +#include +#include +#include + +__BEGIN_DECLS + +/** Not useful on normal Android; disallowed by SELinux. */ +/** useful for partial Android userspace components running inside the unmodified kernel of a */ +/** different operating system that doesn't have ashmem, ion, dma-buf or SELinux, */ +/** for example using a container, or other method */ + +void* _Nonnull shmat_vanilla(int __shm_id, const void* _Nullable __addr, int __flags); +int shmctl_vanilla(int __shm_id, int __op, struct shmid64_ds* _Nullable __buf); +int shmdt_vanilla(const void* _Nonnull __addr); +int shmget_vanilla(key_t __key, size_t __size, int __flags); + +__END_DECLS diff --git a/shmem.c b/shmem.c index f95fc07..fd84cec 100644 --- a/shmem.c +++ b/shmem.c @@ -21,6 +21,7 @@ #endif #include "shm.h" +#include "shm_vanilla.h" #define DBG(...) __android_log_print(ANDROID_LOG_INFO, "shmem", __VA_ARGS__) #define ASHV_KEY_SYMLINK_PATH _PATH_TMP "ashv_key_%d" @@ -153,6 +154,21 @@ static int ashmem_create_region(char const* name, size_t size) #endif } +static bool ashmem_is_available(void) { +#if __ANDROID_API__ >= 26 + int fd = ASharedMemory_create("libandroid-shmem-test", 1); + if (fd == -1) { +#else + if (access("/dev/ashmem", F_OK) != 0) { +#endif + return false; + } +#if __ANDROID_API__ >= 26 + close(fd); +#endif + return true; +} + static void ashv_check_pid() { pid_t mypid = getpid(); @@ -296,7 +312,9 @@ static int ashv_read_remote_segment(int shmid) /* Get shared memory area identifier. */ int shmget(key_t key, size_t size, int flags) { - (void) flags; + if (!ashmem_is_available()) { + return shmget_vanilla(key, size, flags); + } ashv_check_pid(); @@ -441,6 +459,10 @@ int shmget(key_t key, size_t size, int flags) /* Attach shared memory segment. */ void* shmat(int shmid, void const* shmaddr, int shmflg) { + if (!ashmem_is_available()) { + return shmat_vanilla(shmid, shmaddr, shmflg); + } + ashv_check_pid(); int socket_id = ashv_socket_id_from_shmid(shmid); @@ -479,6 +501,10 @@ void* shmat(int shmid, void const* shmaddr, int shmflg) /* Detach shared memory segment. */ int shmdt(void const* shmaddr) { + if (!ashmem_is_available()) { + return shmdt_vanilla(shmaddr); + } + ashv_check_pid(); pthread_mutex_lock(&mutex); @@ -561,6 +587,10 @@ int libandroid_shmdt_fd(int fd) /* Shared memory control operation. */ int shmctl(int shmid, int cmd, struct shmid_ds *buf) { + if (!ashmem_is_available()) { + return shmctl_vanilla(shmid, cmd, buf); + } + ashv_check_pid(); if (cmd == IPC_RMID) { diff --git a/shmem_vanilla.c b/shmem_vanilla.c new file mode 100644 index 0000000..2871866 --- /dev/null +++ b/shmem_vanilla.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/** fork of https://android.googlesource.com/platform/bionic/+/02ce401d1e2b31586c94c8b6999364bbbce27388/libc/bionic/sys_shm.cpp */ + +#include +#include + +#include "shm_vanilla.h" + +void* shmat_vanilla(int id, const void* address, int flags) { + return (void*)(syscall(SYS_shmat, id, address, flags)); +} + +int shmctl_vanilla(int id, int cmd, struct shmid64_ds* buf) { + // upstream bionic libc and also other libc implementations contain + // this code that ORs cmd with IPC_64, but unfortunately, for some reason + // in 32-bit termux-docker, an error always occurs during test-with-key + // 'shmctl: Invalid argument' unless the ORing with IPC_64 is disabled. + // when that is disabled, all tests pass. This result holds for both arm and i686 + // termux-docker running in both fully 32-bit kernels and the 32-bit mode of + // 64-bit kernels. +#if 0 +//#if !defined(__LP64__) + // Annoyingly, the kernel requires this for 32-bit but rejects it for 64-bit. + cmd |= IPC_64; +#endif + return syscall(SYS_shmctl, id, cmd, buf); +} + +int shmdt_vanilla(const void* address) { + return syscall(SYS_shmdt, address); +} + +int shmget_vanilla(key_t key, size_t size, int flags) { + return syscall(SYS_shmget, key, size, flags); +} diff --git a/test/Makefile b/test/Makefile index fbb54a2..35ecc94 100644 --- a/test/Makefile +++ b/test/Makefile @@ -4,11 +4,11 @@ OS := $(shell uname -o 2> /dev/null) ifeq ($(OS), Android) CFLAGS += -I.. -llog -EXTRA_CFILE = ../shmem.c +EXTRA_CFILES = ../shmem.c ../shmem_vanilla.c cleanup-memory: test ; else CFLAGS += -DSYSV_ASHMEM_TEST_SYSTEM -EXTRA_CFILE = +EXTRA_CFILES = cleanup-memory: test ./cleanup-shared-memory.sh endif @@ -18,7 +18,7 @@ test: $(addprefix testcase-,$(patsubst %.c,%,$(wildcard *.c))) ; testcase-% : test-% ./$^ -test-%: %.c $(EXTRA_CFILE) +test-%: %.c $(EXTRA_CFILES) $(CC) $(CFLAGS) $+ -o $@ clean: