From 5550816f5962943abd81fbf68901dad575f18c06 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Mon, 1 May 2023 09:31:58 +0800 Subject: [PATCH] Work around system restrictions regarding exec * doc/emacs/android.texi (Android Environment): Document `android-use-exec-loader'. * exec/exec1.c (main): Set program group of child process. * src/android.c (android_rewrite_spawn_argv): New function. * src/android.h: Update prototypes. * src/androidfns.c (syms_of_androidfns): New variable `android_use_exec_loader'. * src/callproc.c (emacs_spawn): Rewrite the argument vector to use exec1 if necessary. --- doc/emacs/android.texi | 16 ++++++-- exec/exec1.c | 6 +++ src/android.c | 83 ++++++++++++++++++++++++++++++++++++++++++ src/android.h | 5 +++ src/androidfns.c | 14 +++++++ src/callproc.c | 16 ++++++++ 6 files changed, 136 insertions(+), 4 deletions(-) diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index 08897d3f97e..e1c644d6043 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -252,10 +252,7 @@ Emacs, the system has an overwhelming number of users. which is the app data directory (@pxref{Android File System}.) Each application is also prohibited from accessing system -directories, and the app data directories of other applications. In -recent versions of Android, the system also prohibits, for security -reasons, even Emacs itself from running executables inside the app -data directory. +directories, and the app data directories of other applications. Emacs comes with several binaries. While being executable files, they are packaged as libraries in the library directory, because @@ -277,6 +274,17 @@ However, the approach it takes was devised by reading Android source code, and is not sanctioned by the Android compatibility definition documents, so your mileage may vary. +@cindex call-process, Android +@vindex android-use-exec-loader + Android 10 and later versions of the system also prohibit Emacs +itself from running executables inside the app data directory. On +these systems, Emacs normally applies a workaround; however, this +workaround requires running all sub-processes in another subprocess, +and applying process tracing to all executables, which may prove to be +problematic for various different reasons. In that case, the +workaround can be disabled by changing the variable +@code{android-use-exec-loader} to @code{nil}. + @section Running Emacs in the background @cindex emacs killed, android @cindex emacs in the background, android diff --git a/exec/exec1.c b/exec/exec1.c index 835bf8e72b9..d77ca8adf54 100644 --- a/exec/exec1.c +++ b/exec/exec1.c @@ -20,6 +20,7 @@ along with GNU Emacs. If not, see . */ #include #include #include +#include #include #include "exec.h" @@ -41,10 +42,15 @@ main (int argc, char **argv) extern char **environ; int wstatus; + pid1 = getpid (); pid = fork (); if (!pid) { + /* Set the process group used to the parent. */ + if (setpgid (0, pid1)) + perror ("setpgid"); + tracing_execve (argv[2], argv + 2, environ); /* An error occured. Exit with failure. */ diff --git a/src/android.c b/src/android.c index 3798758ff16..ce8f277e120 100644 --- a/src/android.c +++ b/src/android.c @@ -6514,6 +6514,89 @@ android_free_cursor (android_cursor cursor) android_destroy_handle (cursor); } + + +/* Process execution. + + Newer Android systems use SELinux to restrict user programs from + executing programs installed in the application data directory for + security reasons. Emacs uses a `loader' binary installed in the + application data directory to manually load executables and replace + the `execve' system call. */ + +enum + { + /* Maximum number of arguments available. */ + MAXARGS = 1024, + }; + +/* Rewrite the command line given in *ARGV to utilize the `exec1' + bootstrap binary if necessary. + + Value is 0 upon success, else 1. Set errno upon failure. + + ARGV holds a pointer to a NULL-terminated array of arguments given + to `emacs_spawn'. */ + +int +android_rewrite_spawn_argv (const char ***argv) +{ + static const char *new_args[MAXARGS]; + static char exec1_name[PATH_MAX], loader_name[PATH_MAX]; + size_t i, nargs; + + /* This isn't required on Android 9 or earlier. */ + + if (android_api_level < 29 || !android_use_exec_loader) + return 0; + + /* Get argv[0]; this should never be NULL. + Then, verify that it exists and is executable. */ + + eassert (**argv); + if (access (**argv, R_OK | X_OK)) + return 1; + + /* Count the number of arguments in *argv. */ + + nargs = 0; + while ((*argv)[nargs]) + ++nargs; + + /* nargs now holds the number of arguments in argv. If it's larger + than MAXARGS, return failure. */ + + if (nargs + 2 > MAXARGS) + { + errno = E2BIG; + return 1; + } + + /* Fill in the name of `libexec1.so'. */ + snprintf (exec1_name, PATH_MAX, "%s/libexec1.so", + android_lib_dir); + + /* And libloader.so. */ + snprintf (loader_name, PATH_MAX, "%s/libloader.so", + android_lib_dir); + + /* Now fill in the first two arguments. */ + new_args[0] = exec1_name; + new_args[1] = loader_name; + + /* And insert the rest. */ + for (i = 0; i < nargs; ++i) + new_args[i + 2] = (*argv)[i]; + + /* Replace argv. */ + *argv = new_args; + + /* Return success. */ + return 0; +} + + + #else /* ANDROID_STUBIFY */ /* X emulation functions for Android. */ diff --git a/src/android.h b/src/android.h index 24666aaf989..62d420d4cce 100644 --- a/src/android.h +++ b/src/android.h @@ -190,6 +190,11 @@ extern void android_write_event (union android_event *); extern unsigned int event_serial; + + +/* Process related functions. */ +extern int android_rewrite_spawn_argv (const char ***); + #endif /* JNI functions should not be built when Emacs is stubbed out for the diff --git a/src/androidfns.c b/src/androidfns.c index 3367ebdf755..3bd34edd5b9 100644 --- a/src/androidfns.c +++ b/src/androidfns.c @@ -3112,6 +3112,20 @@ Note that if you set this, you will no longer be able to quit Emacs using the volume down button. */); android_pass_multimedia_buttons_to_system = false; + DEFVAR_BOOL ("android-use-exec-loader", android_use_exec_loader, + doc: /* Whether or not to bypass system restrictions on program execution. + +Android 10 and later prevent programs from executing files installed +in writable directories, such as the application data directory. + +When non-nil, Emacs will bypass this restriction by running such +executables under system call tracing, and replacing the `execve' +system call with a version which ignores the system's security +restrictions. + +This option has no effect on Android 9 and earlier. */); + android_use_exec_loader = true; + /* Functions defined. */ defsubr (&Sx_create_frame); defsubr (&Sxw_color_defined_p); diff --git a/src/callproc.c b/src/callproc.c index a1811a3bb23..015b52bc9bc 100644 --- a/src/callproc.c +++ b/src/callproc.c @@ -92,6 +92,10 @@ extern char **environ; #include "pgtkterm.h" #endif +#ifdef HAVE_ANDROID +#include "android.h" +#endif /* HAVE_ANDROID */ + /* Pattern used by call-process-region to make temp files. */ static Lisp_Object Vtemp_file_name_pattern; @@ -1437,6 +1441,18 @@ emacs_spawn (pid_t *newpid, int std_in, int std_out, int std_err, const char *pty_name, bool pty_in, bool pty_out, const sigset_t *oldset) { +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY + /* Android 10 and later don't allow directly executing programs + installed in the application data directory. Emacs provides a + loader binary which replaces the `execve' system call for it and + all its children. On these systems, rewrite the command line to + call that loader binary instead. */ + + if (android_rewrite_spawn_argv ((const char ***) &argv)) + return 1; +#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */ + + #if USABLE_POSIX_SPAWN /* Prefer the simpler `posix_spawn' if available. `posix_spawn' doesn't yet support setting up pseudoterminals, so we fall back -- 2.39.2