From b0819f54ef62b86e699e20d2343f1cf23839fd1d Mon Sep 17 00:00:00 2001 From: Dimitris Panokostas Date: Tue, 21 Jul 2026 21:47:35 +0300 Subject: [PATCH 1/5] fix(uaelib): gate host integration access Host shell traps bypassed the Native Code preference, allowing guest commands after host-run was denied. Gate all host execution and session operations, and terminate active sessions when access is revoked or the emulator resets. Keep session close and platform discovery available for cleanup and compatibility. Report Windows pipe session status through the existing process-handle implementation. --- src/devices.cpp | 6 ++++ src/include/uae.h | 1 + src/osdep/gfx_prefs_check.cpp | 2 ++ src/uaelib.cpp | 60 +++++++++++++++++++++++++++-------- 4 files changed, 56 insertions(+), 13 deletions(-) diff --git a/src/devices.cpp b/src/devices.cpp index 481f37b38..cc159c9e9 100644 --- a/src/devices.cpp +++ b/src/devices.cpp @@ -238,6 +238,9 @@ void devices_reset_ext(int hardreset) void devices_reset(int hardreset) { memset(device_reset_done, 0, sizeof(device_reset_done)); +#ifdef AMIBERRY + uaelib_host_cleanup(); +#endif // must be first init_eventtab(); init_shm(); @@ -385,6 +388,9 @@ void devices_update_sync(float svpos, float syncadjust) void virtualdevice_free(void) { +#ifdef AMIBERRY + uaelib_host_cleanup(); +#endif #ifdef WITH_PPC // must be first uae_ppc_free(); diff --git a/src/include/uae.h b/src/include/uae.h index e77d1c5fa..da49f0210 100644 --- a/src/include/uae.h +++ b/src/include/uae.h @@ -115,6 +115,7 @@ void filesys_addexternals (void); #ifdef AMIBERRY extern std::vector get_cd_drives(); extern int add_filesys_unit(struct uaedev_config_info* ci, bool custom); +extern void uaelib_host_cleanup(); #endif #endif /* UAE_UAE_H */ diff --git a/src/osdep/gfx_prefs_check.cpp b/src/osdep/gfx_prefs_check.cpp index 0784eb7a1..fd323640a 100644 --- a/src/osdep/gfx_prefs_check.cpp +++ b/src/osdep/gfx_prefs_check.cpp @@ -569,6 +569,8 @@ int check_prefs_changed_gfx() currprefs.minimized_pause = changed_prefs.minimized_pause; currprefs.minimized_input = changed_prefs.minimized_input; currprefs.capture_always = changed_prefs.capture_always; + if (currprefs.native_code && !changed_prefs.native_code) + uaelib_host_cleanup(); currprefs.native_code = changed_prefs.native_code; currprefs.alt_tab_release = changed_prefs.alt_tab_release; currprefs.ctrl_alt_release = changed_prefs.ctrl_alt_release; diff --git a/src/uaelib.cpp b/src/uaelib.cpp index 98b9efef8..7cba87269 100644 --- a/src/uaelib.cpp +++ b/src/uaelib.cpp @@ -433,6 +433,30 @@ static const uae_u32 HOST_SHELL_STATUS_RUNNING = 1; static const uae_u32 HOST_SHELL_STATUS_EXITED = 0x80000000; static const uae_u32 HOST_SHELL_IO_MAX = 4096; +static constexpr bool uaelib_host_trap_requires_native_code(uae_u32 trap) +{ + return trap >= 88 && trap <= 95 && trap != 93; +} + +static constexpr uae_u32 uaelib_host_trap_denied_result(uae_u32 trap) +{ + return trap == 91 || trap == 92 ? static_cast(-1) : 0; +} + +static_assert(uaelib_host_trap_requires_native_code(88)); +static_assert(uaelib_host_trap_requires_native_code(89)); +static_assert(uaelib_host_trap_requires_native_code(90)); +static_assert(uaelib_host_trap_requires_native_code(91)); +static_assert(uaelib_host_trap_requires_native_code(92)); +static_assert(!uaelib_host_trap_requires_native_code(93)); +static_assert(uaelib_host_trap_requires_native_code(94)); +static_assert(uaelib_host_trap_requires_native_code(95)); +static_assert(!uaelib_host_trap_requires_native_code(96)); +static_assert(uaelib_host_trap_denied_result(90) == 0); +static_assert(uaelib_host_trap_denied_result(91) == static_cast(-1)); +static_assert(uaelib_host_trap_denied_result(92) == static_cast(-1)); +static_assert(uaelib_host_trap_denied_result(94) == HOST_SHELL_STATUS_INVALID); + #if !defined(_WIN32) static int host_shell_exit_code(int status) { @@ -866,6 +890,12 @@ static uae_u32 uaelib_host_close(TrapContext* ctx, uae_u32 handle) #endif } +void uaelib_host_cleanup() +{ + while (!shell_sessions.empty()) + uaelib_host_close(nullptr, shell_sessions.begin()->first); +} + // Host platform ids reported by trap 96; 0 (unknown trap on older // builds) means the guest should assume a POSIX host. static uae_u32 uaelib_host_get_platform(void) @@ -882,15 +912,11 @@ static uae_u32 uaelib_host_get_platform(void) static uae_u32 uaelib_host_status(TrapContext* ctx, uae_u32 handle) { (void)ctx; -#if defined(_WIN32) - return HOST_SHELL_STATUS_INVALID; -#else if (shell_sessions.find(handle) == shell_sessions.end()) return HOST_SHELL_STATUS_INVALID; ShellSession& session = shell_sessions[handle]; return host_shell_update_status(session); -#endif } static std::string quote_path(const char* path) { @@ -1033,6 +1059,11 @@ static uae_u32 uaelib_midi(TrapContext *ctx, uae_u32 op, uae_u32 index, uaecptr static uae_u32 uaelib_demux_common(TrapContext *ctx, uae_u32 ARG0, uae_u32 ARG1, uae_u32 ARG2, uae_u32 ARG3, uae_u32 ARG4, uae_u32 ARG5) { write_log("%ld\n",ARG0); + +#ifdef AMIBERRY + if (!currprefs.native_code && uaelib_host_trap_requires_native_code(ARG0)) + return uaelib_host_trap_denied_result(ARG0); +#endif switch (ARG0) { case 0: return emulib_GetVersion(); @@ -1090,17 +1121,20 @@ static uae_u32 uaelib_demux_common(TrapContext *ctx, uae_u32 ARG0, uae_u32 ARG1, } #ifdef AMIBERRY case 88: - if (currprefs.native_code) return emulib_execute_on_host(ctx, ARG1); - return 0; - case 89: return uaelib_host_view(ctx, ARG1); - - case 90: return uaelib_host_open(ctx, ARG1); - case 91: return uaelib_host_read(ctx, ARG1, ARG2, ARG3); - case 92: return uaelib_host_write(ctx, ARG1, ARG2, ARG3); + case 89: + return uaelib_host_view(ctx, ARG1); + case 90: + return uaelib_host_open(ctx, ARG1); + case 91: + return uaelib_host_read(ctx, ARG1, ARG2, ARG3); + case 92: + return uaelib_host_write(ctx, ARG1, ARG2, ARG3); case 93: return uaelib_host_close(ctx, ARG1); - case 94: return uaelib_host_status(ctx, ARG1); - case 95: return uaelib_host_open_pipe(ctx, ARG1); + case 94: + return uaelib_host_status(ctx, ARG1); + case 95: + return uaelib_host_open_pipe(ctx, ARG1); case 96: return uaelib_host_get_platform(); case 100: From 9d9b1060d5cd5ee7c87ed6c1185e23030c95dff0 Mon Sep 17 00:00:00 2001 From: Dimitris Panokostas Date: Tue, 21 Jul 2026 22:01:49 +0300 Subject: [PATCH 2/5] fix(uaelib): bound host session cleanup Processes that ignore SIGTERM must not stall reset, shutdown, or permission revocation. Poll without blocking, then escalate to SIGKILL and stop after a second bounded interval. --- src/uaelib.cpp | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src/uaelib.cpp b/src/uaelib.cpp index 7cba87269..7fb5d1747 100644 --- a/src/uaelib.cpp +++ b/src/uaelib.cpp @@ -432,6 +432,11 @@ static const uae_u32 HOST_SHELL_STATUS_INVALID = 0; static const uae_u32 HOST_SHELL_STATUS_RUNNING = 1; static const uae_u32 HOST_SHELL_STATUS_EXITED = 0x80000000; static const uae_u32 HOST_SHELL_IO_MAX = 4096; +#if !defined(_WIN32) +static const int HOST_SHELL_WAIT_INTERVAL_MS = 10; +static const int HOST_SHELL_TERM_TIMEOUT_MS = 1000; +static const int HOST_SHELL_KILL_TIMEOUT_MS = 1000; +#endif static constexpr bool uaelib_host_trap_requires_native_code(uae_u32 trap) { @@ -521,6 +526,34 @@ static uae_u32 host_shell_update_status(ShellSession& session) #endif } +#if !defined(_WIN32) +static bool host_shell_wait_for_exit(ShellSession& session, int timeout_ms) +{ + for (int elapsed = 0; elapsed < timeout_ms; elapsed += HOST_SHELL_WAIT_INTERVAL_MS) { + if ((host_shell_update_status(session) & HOST_SHELL_STATUS_EXITED) != 0) + return true; + sleep_millis(HOST_SHELL_WAIT_INTERVAL_MS); + } + return (host_shell_update_status(session) & HOST_SHELL_STATUS_EXITED) != 0; +} + +static void host_shell_terminate(ShellSession& session) +{ + if ((host_shell_update_status(session) & HOST_SHELL_STATUS_EXITED) != 0) + return; + + if (kill(session.pid, SIGTERM) < 0 && errno != ESRCH) + write_log("Failed to terminate host shell process %d: %s\n", session.pid, strerror(errno)); + if (host_shell_wait_for_exit(session, HOST_SHELL_TERM_TIMEOUT_MS)) + return; + + if (kill(session.pid, SIGKILL) < 0 && errno != ESRCH) + write_log("Failed to kill host shell process %d: %s\n", session.pid, strerror(errno)); + if (!host_shell_wait_for_exit(session, HOST_SHELL_KILL_TIMEOUT_MS)) + write_log("Timed out waiting for host shell process %d to exit\n", session.pid); +} +#endif + static uae_u32 uaelib_host_open(TrapContext* ctx, uaecptr command) { char cmd[HOST_SHELL_CMD_MAX]; @@ -880,10 +913,7 @@ static uae_u32 uaelib_host_close(TrapContext* ctx, uae_u32 handle) close(session.outfd); session.outfd = -1; } - if ((host_shell_update_status(session) & HOST_SHELL_STATUS_EXITED) == 0) { - kill(session.pid, SIGTERM); - waitpid(session.pid, NULL, 0); - } + host_shell_terminate(session); shell_sessions.erase(handle); return 1; From e1cd14f073a158905a4b971d33a2bb37e59c79f3 Mon Sep 17 00:00:00 2001 From: Dimitris Panokostas Date: Tue, 21 Jul 2026 22:14:28 +0300 Subject: [PATCH 3/5] fix(prefs): apply Native Code changes early Graphics reconfiguration can return before general preferences are copied. Apply Native Code permission changes first so revocation always closes active host sessions. --- src/osdep/gfx_prefs_check.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/osdep/gfx_prefs_check.cpp b/src/osdep/gfx_prefs_check.cpp index fd323640a..afcfa4d76 100644 --- a/src/osdep/gfx_prefs_check.cpp +++ b/src/osdep/gfx_prefs_check.cpp @@ -63,6 +63,13 @@ int check_prefs_changed_gfx() if (!config_changed && !display_change_requested) return 0; + const bool native_code_changed = currprefs.native_code != changed_prefs.native_code; + if (native_code_changed) { + if (currprefs.native_code && !changed_prefs.native_code) + uaelib_host_cleanup(); + currprefs.native_code = changed_prefs.native_code; + } + c |= config_changed_flags; config_changed_flags = 0; @@ -535,7 +542,7 @@ int check_prefs_changed_gfx() currprefs.minimized_pause != changed_prefs.minimized_pause || currprefs.minimized_input != changed_prefs.minimized_input || currprefs.capture_always != changed_prefs.capture_always || - currprefs.native_code != changed_prefs.native_code || + native_code_changed || currprefs.alt_tab_release != changed_prefs.alt_tab_release || currprefs.ctrl_alt_release != changed_prefs.ctrl_alt_release || currprefs.use_retroarch_quit != changed_prefs.use_retroarch_quit || @@ -569,9 +576,6 @@ int check_prefs_changed_gfx() currprefs.minimized_pause = changed_prefs.minimized_pause; currprefs.minimized_input = changed_prefs.minimized_input; currprefs.capture_always = changed_prefs.capture_always; - if (currprefs.native_code && !changed_prefs.native_code) - uaelib_host_cleanup(); - currprefs.native_code = changed_prefs.native_code; currprefs.alt_tab_release = changed_prefs.alt_tab_release; currprefs.ctrl_alt_release = changed_prefs.ctrl_alt_release; currprefs.use_retroarch_quit = changed_prefs.use_retroarch_quit; From 0ce2d58dcbf3e5d9fe660e2a5810377a20750af3 Mon Sep 17 00:00:00 2001 From: Dimitris Panokostas Date: Tue, 21 Jul 2026 22:29:45 +0300 Subject: [PATCH 4/5] fix(prefs): process Native Code live toggles Misc checkbox changes do not mark config_changed, so the early guard could skip host-session cleanup until another preference changed. --- src/osdep/gfx_prefs_check.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/osdep/gfx_prefs_check.cpp b/src/osdep/gfx_prefs_check.cpp index afcfa4d76..37f74fc3e 100644 --- a/src/osdep/gfx_prefs_check.cpp +++ b/src/osdep/gfx_prefs_check.cpp @@ -60,9 +60,6 @@ int check_prefs_changed_gfx() int c = 0; bool monitors[MAX_AMIGAMONITORS]{}; - if (!config_changed && !display_change_requested) - return 0; - const bool native_code_changed = currprefs.native_code != changed_prefs.native_code; if (native_code_changed) { if (currprefs.native_code && !changed_prefs.native_code) @@ -70,6 +67,9 @@ int check_prefs_changed_gfx() currprefs.native_code = changed_prefs.native_code; } + if (!config_changed && !display_change_requested && !native_code_changed) + return 0; + c |= config_changed_flags; config_changed_flags = 0; From afde7e5b722be6ba3aaa5a9374803a2db5de0c20 Mon Sep 17 00:00:00 2001 From: Dimitris Panokostas Date: Wed, 22 Jul 2026 13:44:57 +0300 Subject: [PATCH 5/5] fix(prefs): preserve unrelated pending changes Native Code toggles do not mark config_changed. Return after applying the toggle so unrelated pending preferences remain staged. --- src/osdep/gfx_prefs_check.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/osdep/gfx_prefs_check.cpp b/src/osdep/gfx_prefs_check.cpp index 37f74fc3e..19172eaf9 100644 --- a/src/osdep/gfx_prefs_check.cpp +++ b/src/osdep/gfx_prefs_check.cpp @@ -67,7 +67,7 @@ int check_prefs_changed_gfx() currprefs.native_code = changed_prefs.native_code; } - if (!config_changed && !display_change_requested && !native_code_changed) + if (!config_changed && !display_change_requested) return 0; c |= config_changed_flags;