mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-11 19:11:08 +00:00
* Run programs, scripts and AppImages from the desktop Windows programs open with the Proton Steam has chosen, and a Steam game's own launch program is handed to Steam. x86-64 Linux programs and game scripts run under FEX, and an exec the kernel refuses goes to droiddeck-open through libblsession's binfmt.c. Programs on noexec shared storage pass KIO's checks; a folder of Linux programs there, and sharun AppImages, are added by the app once and run from the copy. Executable scripts run without KIO's prompt, mpv plays on the software desktop, FEXServer's socket moves under XDG_RUNTIME_DIR, and Proton runtimes no longer show up as games. * Switch to Desktop from Big Picture without Deck mode Big Picture's Switch to Desktop asks the client's SteamOS manager, which the client only has when started as SteamOS (Deck mode). Without it the request failed and the screen stayed on "Switching to Desktop..." for good. The live compat helper now sees the request on its way to the client and hands it to steamos-session-select, which opens the KDE desktop as Deck mode already did. * Run x86 programs from shared storage where they lie FEX asks for the guest's PROT_EXEC when it maps a program or library, which the kernel refuses on noexec shared storage; FEX never runs those pages itself, so libblsession maps them without it in FEX's processes. An x86 game, its libraries and its start script now run in place instead of being copied in; only native code on noexec storage is still copied. A Steam library's games are never copied, and a Linux game's launch program opened from the desktop goes to Steam like a Windows one.
66 lines
2.2 KiB
C
66 lines
2.2 KiB
C
/*
|
|
* noexec.c: FEX runs an x86 program and its libraries where they lie, on noexec shared storage too.
|
|
* FEX maps them with the guest's PROT_EXEC, which the kernel refuses for a file on a noexec mount,
|
|
* but it never runs those pages itself - it runs its own translation of them - so a mapping or an
|
|
* mprotect refused for that alone is made without PROT_EXEC. Only in FEX's own processes.
|
|
*/
|
|
#define _GNU_SOURCE
|
|
#include <dlfcn.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/statvfs.h>
|
|
#include <unistd.h>
|
|
|
|
/* NOEXEC_TEST: tools/tests builds it for the host. */
|
|
#if defined(__aarch64__) || defined(NOEXEC_TEST)
|
|
|
|
static int in_fex(void) {
|
|
static int known = -1;
|
|
if (known < 0) {
|
|
char exe[512];
|
|
ssize_t n = readlink("/proc/self/exe", exe, sizeof exe - 1);
|
|
exe[n > 0 ? n : 0] = 0;
|
|
const char *name = strrchr(exe, '/');
|
|
name = name ? name + 1 : exe;
|
|
known = !strcmp(name, "FEX") || !strcmp(name, "FEXInterpreter") || !strcmp(name, "FEXLoader");
|
|
}
|
|
return known;
|
|
}
|
|
|
|
/* After an mmap failed (fsync.c's mmap asks): whether to map it again without PROT_EXEC. */
|
|
__attribute__((visibility("hidden"))) int bl_noexec_retry(int prot, int flags, int fd) {
|
|
int error = errno;
|
|
struct statvfs vfs;
|
|
int retry = (error == EACCES || error == EPERM) && (prot & PROT_EXEC) && fd >= 0 && !(flags & MAP_ANONYMOUS) &&
|
|
in_fex() && fstatvfs(fd, &vfs) == 0 && (vfs.f_flag & ST_NOEXEC);
|
|
errno = error;
|
|
return retry;
|
|
}
|
|
|
|
static int (*real_mprotect)(void *, size_t, int);
|
|
|
|
/* Looked up before anything runs: Wine calls mprotect from its signal handlers, where dlsym is unsafe. */
|
|
__attribute__((constructor)) static void noexec_init(void) {
|
|
real_mprotect = (int (*)(void *, size_t, int))dlsym(RTLD_NEXT, "mprotect");
|
|
}
|
|
|
|
int mprotect(void *addr, size_t len, int prot) {
|
|
if (!real_mprotect) noexec_init();
|
|
int result = real_mprotect(addr, len, prot);
|
|
if (result && errno == EACCES && (prot & PROT_EXEC) && in_fex()) {
|
|
result = real_mprotect(addr, len, prot & ~PROT_EXEC);
|
|
if (result) errno = EACCES;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#else
|
|
|
|
__attribute__((visibility("hidden"))) int bl_noexec_retry(int prot, int flags, int fd) {
|
|
(void)prot, (void)flags, (void)fd;
|
|
return 0;
|
|
}
|
|
|
|
#endif
|