mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-11 02:51:10 +00:00
Every Electron AppImage hung without a window or crashed at its first page. Four causes, each enough on its own: - The session preload's hooks found the real functions with dlsym(RTLD_NEXT). Before glibc 2.34 a thread's first dlsym callocs, and Chromium's PartitionAlloc calls syscall() from inside its first malloc: the allocator waited for itself. On older glibc (the x86 runtime's 2.31) next.c now walks the libraries after the preload with no allocation and picks each function's default version, as glibc 2.36 does; the old lookup handed realpath() and posix_spawn() their compat versions, and realpath(path, NULL) failed. arm64 keeps dlsym. - libpci's sysfs backend exits when /sys/bus/pci/devices is listable but a device's files are not (a RedMagic's PCIe root port), taking every Chromium GPU process with it. An empty directory is bound over it on devices where that happens. - FEX_OUTPUTLOG=stderr made FEX log to its own duplicate of descriptor 2. Chromium dup2()s its IPC socket onto that low number in each child it starts, then lists /proc/self/fd, which FEX logs into the socket. Empty, FEX writes to descriptor 2 itself. - The tracer preload served every /proc/<pid>/status as a memfd, which is open for writing. Chromium hands each renderer its status as a read-only file and checks that it is one. Only a process's own status (pid, tid, self, thread-self) is rewritten now.
144 lines
4.4 KiB
C
144 lines
4.4 KiB
C
/*
|
|
* get_robust_list(2) for the session's own threads.
|
|
*
|
|
* Android's app policy refuses the call, and Steam's cross-process mutex insists on seeing the
|
|
* head glibc registered. The kernel's list protocol gives it away: a robust mutex this thread
|
|
* holds is linked directly behind the registered head, so locking a private one and following
|
|
* the link back finds the head without depending on libc's layout.
|
|
*/
|
|
#define _GNU_SOURCE
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <linux/futex.h>
|
|
#include <pthread.h>
|
|
#include <stdarg.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <sys/syscall.h>
|
|
#include <sys/uio.h>
|
|
#include <unistd.h>
|
|
|
|
__attribute__((visibility("hidden"))) void *bl_next(const char *name);
|
|
|
|
#ifndef SYS_openat2
|
|
#define SYS_openat2 437
|
|
#endif
|
|
|
|
#define BL_RESOLVE_BENEATH 0x08
|
|
#define BL_RESOLVE_IN_ROOT 0x10
|
|
|
|
struct bl_open_how {
|
|
uint64_t flags;
|
|
uint64_t mode;
|
|
uint64_t resolve;
|
|
};
|
|
|
|
static long (*real_syscall)(long, ...);
|
|
static int openat2_missing;
|
|
|
|
int bl_fsync_syscall(long number, const long *args, long *ret) __attribute__((visibility("hidden")));
|
|
|
|
static struct robust_list_head *thread_robust_head(void) {
|
|
pthread_mutexattr_t attr;
|
|
pthread_mutex_t mutex;
|
|
struct robust_list_head *head = NULL;
|
|
pthread_mutexattr_init(&attr);
|
|
pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST);
|
|
pthread_mutex_init(&mutex, &attr);
|
|
pthread_mutexattr_destroy(&attr);
|
|
if (pthread_mutex_lock(&mutex) != 0) {
|
|
return NULL;
|
|
}
|
|
/* The mutex holds tids and lock words beside the link, so a word is only followed
|
|
* through a read that fails instead of faulting on a non-pointer. */
|
|
uintptr_t lo = (uintptr_t)&mutex, hi = lo + sizeof(mutex);
|
|
for (uintptr_t p = lo; p + sizeof(void *) <= hi && !head; p += sizeof(void *)) {
|
|
struct robust_list_head *candidate = *(struct robust_list_head **)p;
|
|
struct robust_list_head copy;
|
|
struct iovec local = {©, sizeof(copy)}, remote = {candidate, sizeof(copy)};
|
|
if ((uintptr_t)candidate & (sizeof(void *) - 1)) {
|
|
continue;
|
|
}
|
|
if (process_vm_readv(getpid(), &local, 1, &remote, 1, 0) != (ssize_t)sizeof(copy)) {
|
|
continue;
|
|
}
|
|
uintptr_t entry = (uintptr_t)copy.list.next;
|
|
if (entry >= lo && entry < hi && copy.futex_offset < 0 && copy.futex_offset > -256) {
|
|
head = candidate;
|
|
}
|
|
}
|
|
pthread_mutex_unlock(&mutex);
|
|
pthread_mutex_destroy(&mutex);
|
|
return head;
|
|
}
|
|
|
|
static long openat2_via_openat(const long *args) {
|
|
const char *path = (const char *)args[1];
|
|
const struct bl_open_how *how = (const struct bl_open_how *)args[2];
|
|
if (!path || !how || (size_t)args[3] < sizeof(*how)) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
if (how->resolve & BL_RESOLVE_IN_ROOT) {
|
|
while (*path == '/') {
|
|
path++;
|
|
}
|
|
if (!*path) {
|
|
path = ".";
|
|
}
|
|
} else if ((how->resolve & BL_RESOLVE_BENEATH) && *path == '/') {
|
|
errno = EXDEV;
|
|
return -1;
|
|
}
|
|
return real_syscall(SYS_openat, args[0], (long)path, (long)(int)how->flags, (long)how->mode);
|
|
}
|
|
|
|
static int execveat_is_execve(const long *args) {
|
|
const char *path = (const char *)args[1];
|
|
if (!path || (args[4] & (AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW))) {
|
|
return 0;
|
|
}
|
|
return path[0] == '/' || (int)args[0] == AT_FDCWD;
|
|
}
|
|
|
|
long syscall(long number, ...) {
|
|
va_list ap;
|
|
long args[6];
|
|
va_start(ap, number);
|
|
for (int i = 0; i < 6; i++) {
|
|
args[i] = va_arg(ap, long);
|
|
}
|
|
va_end(ap);
|
|
if (number == SYS_get_robust_list && (args[0] == 0 || args[0] == gettid())) {
|
|
struct robust_list_head *head = thread_robust_head();
|
|
if (!head) {
|
|
errno = ENOSYS;
|
|
return -1;
|
|
}
|
|
*(struct robust_list_head **)args[1] = head;
|
|
*(size_t *)args[2] = sizeof(*head);
|
|
return 0;
|
|
}
|
|
long handled;
|
|
if (bl_fsync_syscall(number, args, &handled)) {
|
|
return handled;
|
|
}
|
|
if (!real_syscall) {
|
|
real_syscall = (long (*)(long, ...)) bl_next("syscall");
|
|
}
|
|
if (number == SYS_openat2) {
|
|
if (!__atomic_load_n(&openat2_missing, __ATOMIC_RELAXED)) {
|
|
long fd = real_syscall(number, args[0], args[1], args[2], args[3]);
|
|
if (fd != -1 || errno != ENOSYS) {
|
|
return fd;
|
|
}
|
|
__atomic_store_n(&openat2_missing, 1, __ATOMIC_RELAXED);
|
|
}
|
|
return openat2_via_openat(args);
|
|
}
|
|
if (number == SYS_execveat && execveat_is_execve(args)) {
|
|
return real_syscall(SYS_execve, args[1], args[2], args[3]);
|
|
}
|
|
return real_syscall(number, args[0], args[1], args[2], args[3], args[4], args[5]);
|
|
}
|