From 51aa646488bb573aeeab653f22dbfc50af6623ba Mon Sep 17 00:00:00 2001 From: "S. Zeid" Date: Thu, 27 Jan 2022 19:04:00 -0600 Subject: [PATCH 1/4] runtime: Always use link target for /proc/self/exe When running an AppImage under [gcompat][0] (e.g. on Alpine Linux), `fopen("/proc/self/exe", "rb")` opens the dynamic linker (e.g. `/lib/ld-musl-x86_64.so.1`) instead of the AppImage itself. However, `readlink("/proc/self/exe", ...)` does give the path to the AppImage as expected, even under gcompat. This commit fixes this problem by always using the link target for `/proc/self/exe` in places that read the AppImage instead of the link itself. Without this commit, running an AppImage under gcompat results in [the error message "This doesn't look like a squashfs image." from squashfuse][1]. With this commit, AppImages run as expected under gcompat. In order to make `--appimage-help` and `--appimage-portable-{home,config}` work under gcompat, I also move the calculation of the `fullpath` variable in `main()` to earlier in the function and change `print_help()` and `portable_option()` to use it instead of calculating the fullpath separately. (When `$TARGET_APPIMAGE` is set, since `realpath()` is (already) used instead of `readlink()` in that case, this change could result in a different path being used in help output and when _creating_ the portable home and config directories with the respective command line options, but `fullpath` is already being used to find existing portable directories when running an AppImage, so this should not affect existing portable installations.) For consistency, I also rename the `fullpath` variable in `main()` and the corresponding arguments in `print_help()` and `portable_option()` to `appimage_fullpath`. Fixes #1015 on Alpine Linux systems with gcompat installed, for AppImages made with this commit applied. Tested on Alpine Linux edge x86_64 and postmarketOS (based on Alpine) edge aarch64. [0]: https://git.adelielinux.org/adelie/gcompat/ [1]: https://github.com/vasi/squashfuse/blob/e51978cd6bb5c4d16fae9eee43d0b258f570bb0f/util.c#L81-L82 --- src/runtime.c | 89 ++++++++++++++++++++++----------------------------- 1 file changed, 38 insertions(+), 51 deletions(-) diff --git a/src/runtime.c b/src/runtime.c index bada3af93..553242adc 100644 --- a/src/runtime.c +++ b/src/runtime.c @@ -212,7 +212,7 @@ mkdir_p(const char* const path) } void -print_help(const char *appimage_path) +print_help(const char *appimage_fullpath) { // TODO: "--appimage-list List content from embedded filesystem image\n" fprintf(stderr, @@ -244,27 +244,19 @@ print_help(const char *appimage_path) " and is neither moved nor renamed, the application contained inside this\n" " AppImage to store its data in this directory rather than in your home\n" " directory\n" - , appimage_path); + , appimage_fullpath); } void -portable_option(const char *arg, const char *appimage_path, const char *name) +portable_option(const char *arg, const char *appimage_fullpath, const char *name) { char option[32]; sprintf(option, "appimage-portable-%s", name); if (arg && strcmp(arg, option)==0) { char portable_dir[PATH_MAX]; - char fullpath[PATH_MAX]; - ssize_t length = readlink(appimage_path, fullpath, sizeof(fullpath)); - if (length < 0) { - fprintf(stderr, "Error getting realpath for %s\n", appimage_path); - exit(EXIT_FAILURE); - } - fullpath[length] = '\0'; - - sprintf(portable_dir, "%s.%s", fullpath, name); + sprintf(portable_dir, "%s.%s", appimage_fullpath, name); if (!mkdir(portable_dir, S_IRWXU)) fprintf(stderr, "Portable %s directory created at %s\n", name, portable_dir); else @@ -536,7 +528,15 @@ int main(int argc, char *argv[]) { * functionality specifically for builds used by appimaged. */ if (getenv("TARGET_APPIMAGE") == NULL) { - strcpy(appimage_path, "/proc/self/exe"); + // for some reason, `fopen("/proc/self/exe", "rb")` tries to open + // the dynamic linker when running under gcompat, even though `readlink()` + // gives the right result, so use `readlink()` here + ssize_t len = readlink("/proc/self/exe", appimage_path, sizeof(appimage_path)); + if (len < 0) { + perror("Failed to obtain AppImage path"); + exit(EXIT_EXECERROR); + } + appimage_path[len] = '\0'; strcpy(argv0_path, argv[0]); } else { strcpy(appimage_path, getenv("TARGET_APPIMAGE")); @@ -577,6 +577,24 @@ int main(int argc, char *argv[]) { #endif } + // calculate full path of AppImage + char appimage_fullpath[PATH_MAX]; + + if(getenv("TARGET_APPIMAGE") == NULL) { + // If we are operating on this file itself, then we've already + // expanded the symlink at `/proc/self/exe` in order to work + // around the issue with gcompat described above + strcpy(appimage_fullpath, appimage_path); + } else { + char* abspath = realpath(appimage_path, NULL); + if (abspath == NULL) { + perror("Failed to obtain realpath for $TARGET_APPIMAGE"); + exit(EXIT_EXECERROR); + } + strcpy(appimage_fullpath, abspath); + free(abspath); + } + // temporary directories are required in a few places // therefore we implement the detection of the temp base dir at the top of the code to avoid redundancy char temp_base[PATH_MAX] = P_tmpdir; @@ -599,16 +617,7 @@ int main(int argc, char *argv[]) { /* Print the help and then exit */ if(arg && strcmp(arg,"appimage-help")==0) { - char fullpath[PATH_MAX]; - - ssize_t length = readlink(appimage_path, fullpath, sizeof(fullpath)); - if (length < 0) { - fprintf(stderr, "Error getting realpath for %s\n", appimage_path); - exit(EXIT_EXECERROR); - } - fullpath[length] = '\0'; - - print_help(fullpath); + print_help(appimage_fullpath); exit(0); } @@ -642,28 +651,6 @@ int main(int argc, char *argv[]) { exit(0); } - // calculate full path of AppImage - int length; - char fullpath[PATH_MAX]; - - if(getenv("TARGET_APPIMAGE") == NULL) { - // If we are operating on this file itself - ssize_t len = readlink(appimage_path, fullpath, sizeof(fullpath)); - if (len < 0) { - perror("Failed to obtain absolute path"); - exit(EXIT_EXECERROR); - } - fullpath[len] = '\0'; - } else { - char* abspath = realpath(appimage_path, NULL); - if (abspath == NULL) { - perror("Failed to obtain absolute path"); - exit(EXIT_EXECERROR); - } - strcpy(fullpath, abspath); - free(abspath); - } - if (getenv("APPIMAGE_EXTRACT_AND_RUN") != NULL || (arg && strcmp(arg, "appimage-extract-and-run") == 0)) { char* hexlified_digest = NULL; @@ -727,7 +714,7 @@ int main(int argc, char *argv[]) { new_argv[new_argc] = NULL; /* Setting some environment variables that the app "inside" might use */ - setenv("APPIMAGE", fullpath, 1); + setenv("APPIMAGE", appimage_fullpath, 1); setenv("ARGV0", argv0_path, 1); setenv("APPDIR", prefix, 1); @@ -785,8 +772,8 @@ int main(int argc, char *argv[]) { exit(0); } - portable_option(arg, appimage_path, "home"); - portable_option(arg, appimage_path, "config"); + portable_option(arg, appimage_fullpath, "home"); + portable_option(arg, appimage_fullpath, "config"); // If there is an argument starting with appimage- (but not appimage-mount which is handled further down) // then stop here and print an error message @@ -908,7 +895,7 @@ int main(int argc, char *argv[]) { } /* Setting some environment variables that the app "inside" might use */ - setenv( "APPIMAGE", fullpath, 1 ); + setenv( "APPIMAGE", appimage_fullpath, 1 ); setenv( "ARGV0", argv0_path, 1 ); setenv( "APPDIR", mount_dir, 1 ); @@ -916,7 +903,7 @@ int main(int argc, char *argv[]) { char portable_config_dir[PATH_MAX]; /* If there is a directory with the same name as the AppImage plus ".home", then export $HOME */ - strcpy (portable_home_dir, fullpath); + strcpy (portable_home_dir, appimage_fullpath); strcat (portable_home_dir, ".home"); if(is_writable_directory(portable_home_dir)){ fprintf(stderr, "Setting $HOME to %s\n", portable_home_dir); @@ -924,7 +911,7 @@ int main(int argc, char *argv[]) { } /* If there is a directory with the same name as the AppImage plus ".config", then export $XDG_CONFIG_HOME */ - strcpy (portable_config_dir, fullpath); + strcpy (portable_config_dir, appimage_fullpath); strcat (portable_config_dir, ".config"); if(is_writable_directory(portable_config_dir)){ fprintf(stderr, "Setting $XDG_CONFIG_HOME to %s\n", portable_config_dir); From afe16a75297763c563bda6a7abd1e764b9b472bb Mon Sep 17 00:00:00 2001 From: "S. Zeid" Date: Fri, 28 Jan 2022 18:14:55 -0600 Subject: [PATCH 2/4] runtime: rename `appimage_fullpath` to `appimage_full_path` --- src/runtime.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/runtime.c b/src/runtime.c index 553242adc..28918818a 100644 --- a/src/runtime.c +++ b/src/runtime.c @@ -212,7 +212,7 @@ mkdir_p(const char* const path) } void -print_help(const char *appimage_fullpath) +print_help(const char *appimage_full_path) { // TODO: "--appimage-list List content from embedded filesystem image\n" fprintf(stderr, @@ -244,11 +244,11 @@ print_help(const char *appimage_fullpath) " and is neither moved nor renamed, the application contained inside this\n" " AppImage to store its data in this directory rather than in your home\n" " directory\n" - , appimage_fullpath); + , appimage_full_path); } void -portable_option(const char *arg, const char *appimage_fullpath, const char *name) +portable_option(const char *arg, const char *appimage_full_path, const char *name) { char option[32]; sprintf(option, "appimage-portable-%s", name); @@ -256,7 +256,7 @@ portable_option(const char *arg, const char *appimage_fullpath, const char *name if (arg && strcmp(arg, option)==0) { char portable_dir[PATH_MAX]; - sprintf(portable_dir, "%s.%s", appimage_fullpath, name); + sprintf(portable_dir, "%s.%s", appimage_full_path, name); if (!mkdir(portable_dir, S_IRWXU)) fprintf(stderr, "Portable %s directory created at %s\n", name, portable_dir); else @@ -578,20 +578,20 @@ int main(int argc, char *argv[]) { } // calculate full path of AppImage - char appimage_fullpath[PATH_MAX]; + char appimage_full_path[PATH_MAX]; if(getenv("TARGET_APPIMAGE") == NULL) { // If we are operating on this file itself, then we've already // expanded the symlink at `/proc/self/exe` in order to work // around the issue with gcompat described above - strcpy(appimage_fullpath, appimage_path); + strcpy(appimage_full_path, appimage_path); } else { char* abspath = realpath(appimage_path, NULL); if (abspath == NULL) { perror("Failed to obtain realpath for $TARGET_APPIMAGE"); exit(EXIT_EXECERROR); } - strcpy(appimage_fullpath, abspath); + strcpy(appimage_full_path, abspath); free(abspath); } @@ -617,7 +617,7 @@ int main(int argc, char *argv[]) { /* Print the help and then exit */ if(arg && strcmp(arg,"appimage-help")==0) { - print_help(appimage_fullpath); + print_help(appimage_full_path); exit(0); } @@ -714,7 +714,7 @@ int main(int argc, char *argv[]) { new_argv[new_argc] = NULL; /* Setting some environment variables that the app "inside" might use */ - setenv("APPIMAGE", appimage_fullpath, 1); + setenv("APPIMAGE", appimage_full_path, 1); setenv("ARGV0", argv0_path, 1); setenv("APPDIR", prefix, 1); @@ -772,8 +772,8 @@ int main(int argc, char *argv[]) { exit(0); } - portable_option(arg, appimage_fullpath, "home"); - portable_option(arg, appimage_fullpath, "config"); + portable_option(arg, appimage_full_path, "home"); + portable_option(arg, appimage_full_path, "config"); // If there is an argument starting with appimage- (but not appimage-mount which is handled further down) // then stop here and print an error message @@ -895,7 +895,7 @@ int main(int argc, char *argv[]) { } /* Setting some environment variables that the app "inside" might use */ - setenv( "APPIMAGE", appimage_fullpath, 1 ); + setenv( "APPIMAGE", appimage_full_path, 1 ); setenv( "ARGV0", argv0_path, 1 ); setenv( "APPDIR", mount_dir, 1 ); @@ -903,7 +903,7 @@ int main(int argc, char *argv[]) { char portable_config_dir[PATH_MAX]; /* If there is a directory with the same name as the AppImage plus ".home", then export $HOME */ - strcpy (portable_home_dir, appimage_fullpath); + strcpy (portable_home_dir, appimage_full_path); strcat (portable_home_dir, ".home"); if(is_writable_directory(portable_home_dir)){ fprintf(stderr, "Setting $HOME to %s\n", portable_home_dir); @@ -911,7 +911,7 @@ int main(int argc, char *argv[]) { } /* If there is a directory with the same name as the AppImage plus ".config", then export $XDG_CONFIG_HOME */ - strcpy (portable_config_dir, appimage_fullpath); + strcpy (portable_config_dir, appimage_full_path); strcat (portable_config_dir, ".config"); if(is_writable_directory(portable_config_dir)){ fprintf(stderr, "Setting $XDG_CONFIG_HOME to %s\n", portable_config_dir); From 3c78618b718e8e0c1a81f285d5e5804f6eedd26e Mon Sep 17 00:00:00 2001 From: "S. Zeid" Date: Tue, 1 Feb 2022 14:09:35 -0600 Subject: [PATCH 3/4] runtime: check that `/proc/self/exe` target was not truncated --- src/runtime.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/runtime.c b/src/runtime.c index 28918818a..1af484ca8 100644 --- a/src/runtime.c +++ b/src/runtime.c @@ -517,7 +517,7 @@ bool build_mount_point(char* mount_dir, const char* const argv0, char const* con } int main(int argc, char *argv[]) { - char appimage_path[PATH_MAX]; + char appimage_path[PATH_MAX + 1]; char argv0_path[PATH_MAX]; char * arg; @@ -536,6 +536,10 @@ int main(int argc, char *argv[]) { perror("Failed to obtain AppImage path"); exit(EXIT_EXECERROR); } + if (len == sizeof(appimage_path)) { + fprintf(stderr, "AppImage path is too long (%d bytes, and PATH_MAX is %d bytes)", len, sizeof(appimage_path)); + exit(EXIT_EXECERROR); + } appimage_path[len] = '\0'; strcpy(argv0_path, argv[0]); } else { From 350b76f244fec8a982e2035b1bbe73870e14c595 Mon Sep 17 00:00:00 2001 From: "S. Zeid" Date: Tue, 1 Feb 2022 14:45:01 -0600 Subject: [PATCH 4/4] runtime: reword comment about `readlink("/proc/self/exe", ...)` --- src/runtime.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/runtime.c b/src/runtime.c index 1af484ca8..83e08cbf6 100644 --- a/src/runtime.c +++ b/src/runtime.c @@ -528,9 +528,9 @@ int main(int argc, char *argv[]) { * functionality specifically for builds used by appimaged. */ if (getenv("TARGET_APPIMAGE") == NULL) { - // for some reason, `fopen("/proc/self/exe", "rb")` tries to open - // the dynamic linker when running under gcompat, even though `readlink()` - // gives the right result, so use `readlink()` here + // when running under gcompat (e.g. on Alpine Linux), + // `fopen("/proc/self/exe", "rb")` tries to open the dynamic linker, even + // though `readlink()` gives the right result, so use `readlink()` here ssize_t len = readlink("/proc/self/exe", appimage_path, sizeof(appimage_path)); if (len < 0) { perror("Failed to obtain AppImage path");