From 483cc10266357877dda6b3760e87f6424a4e4a26 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Fri, 25 Sep 2026 00:49:00 +0800 Subject: [PATCH] fix(appimage): build AppRun with bounded argument splitting (#16330) * fix(appimage): build AppRun with bounded argument splitting AppRun v2.0.0, which appimage-builder downloads, copies each argument into a 1 KiB stack buffer in apprun_shell_split_arguments, so any argument of 1024 bytes or more aborts the AppImage with "buffer overflow detected" before RustDesk starts (e.g. --connect with a 10000-char id). Build AppRun from the v2.0.0 commit with a patch that sizes the buffer from the input and stops the quote/escape scanning at the terminating NUL, in ubuntu:16.04 like upstream's release, and put it where appimage-builder looks before downloading. Co-Authored-By: Claude Opus 5.5 (1M context) * fix(appimage): verify the patched AppRun is the one shipped Reset the split buffer by its first byte rather than zeroing all of it per argument, check the AppRun source tarball's sha256, pin the build image by digest, and fail the job if appimage-builder deployed anything other than the binary built here. Co-Authored-By: Claude Opus 5.5 (1M context) --------- Co-authored-by: Claude Opus 5.5 (1M context) --- .github/workflows/flutter-build.yml | 2 + appimage/apprun-split-arguments.patch | 55 +++++++++++++++++++++++++++ appimage/build-apprun.sh | 38 ++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 appimage/apprun-split-arguments.patch create mode 100755 appimage/build-apprun.sh diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 4199ae201..1c4520522 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -2444,7 +2444,9 @@ jobs: sudo pip3 install git+https://github.com/rustdesk-org/appimage-builder.git # run appimage-builder pushd appimage + ./build-apprun.sh ${{ matrix.job.arch }} sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-${{ matrix.job.arch }}.yml + cmp AppDir/AppRun appimage-build/AppRun/v2.0.0/AppRun-Release-${{ matrix.job.arch }} - name: Publish appimage package if: env.UPLOAD_ARTIFACT == 'true' diff --git a/appimage/apprun-split-arguments.patch b/appimage/apprun-split-arguments.patch new file mode 100644 index 000000000..e9e68959e --- /dev/null +++ b/appimage/apprun-split-arguments.patch @@ -0,0 +1,55 @@ +--- a/src/common/shell_utils.c ++++ b/src/common/shell_utils.c +@@ -201,7 +201,8 @@ + + const char *begin = str; + const char *end = str; +- char buffer[1024] = {0x0}; ++ size_t buffer_size = strlen(str) + 1; ++ char *buffer = calloc(buffer_size, sizeof(char)); + + while (*begin != '\0') { + begin = apprun_string_consume_spaces(begin); +@@ -209,7 +210,7 @@ + + if (*begin == '\\') { + strncat(buffer, begin + 1, 1); +- end = begin + 2; ++ end = begin[1] != '\0' ? begin + 2 : begin + 1; + consumed = true; + } + +@@ -218,7 +219,8 @@ + strncat(buffer, begin + 1, end - begin - 1); + consumed = true; + +- end++; ++ if (*end != '\0') ++ end++; + } + + if (!consumed && *begin == '\"') { +@@ -226,7 +228,8 @@ + strncat(buffer, begin + 1, end - begin - 1); + consumed = true; + +- end++; ++ if (*end != '\0') ++ end++; + } + + if (!consumed && *begin != '\'' && *begin != '\"' && *begin != '\\') { +@@ -240,11 +243,12 @@ + split_count++; + } + +- memset(buffer, 0, 1024); ++ buffer[0] = '\0'; + } + + begin = end; + } + ++ free(buffer); + return splits; + } diff --git a/appimage/build-apprun.sh b/appimage/build-apprun.sh new file mode 100755 index 000000000..16426f983 --- /dev/null +++ b/appimage/build-apprun.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +# AppRun v2.0.0, which appimage-builder downloads, splits every argument into a 1 KiB stack +# buffer, so any argument of 1024 bytes or more aborts the AppImage before RustDesk starts. +# Build it with apprun-split-arguments.patch into the path appimage-builder checks before +# downloading. ubuntu:16.04 is what upstream released from, so the host glibc floor is unchanged. +set -euo pipefail + +arch=$1 +version=v2.0.0 +commit=407700ccef58e1a64cc49856074aee4b1c75d599 +sha256=05729f2ae0a744369d2d19d0f37acfbdcb1ee064172955b7576883ccef5113b3 +image=ubuntu:16.04@sha256:1f1a2d56de1d604801a9671f301190704c25d604a416f59e03c04f5c6ffee0d6 +here=$(cd "$(dirname "$0")" && pwd) + +case $arch in + x86_64) packages="cmake make g++" ;; + aarch64) packages="cmake make g++ g++-aarch64-linux-gnu" ;; + *) echo "Unsupported arch: $arch" >&2; exit 1 ;; +esac + +tmp=$(mktemp -d) +src=$tmp/src +curl -fsSL -o "$tmp/apprun.tar.gz" "https://github.com/AppImageCrafters/AppRun/archive/$commit.tar.gz" +echo "$sha256 $tmp/apprun.tar.gz" | sha256sum -c - +mkdir "$src" +tar -xzf "$tmp/apprun.tar.gz" -C "$src" --strip-components=1 +patch -d "$src" -p1 < "$here/apprun-split-arguments.patch" + +docker run --rm -v "$src:/src" -w /src -e DEBIAN_FRONTEND=noninteractive "$image" bash -c " + set -e + apt-get update + apt-get install -y --no-install-recommends $packages + mkdir build && cd build + cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=../cmake/$arch-toolchain.cmake + cmake --build . --target AppRun" + +mkdir -p "$here/appimage-build/AppRun/$version" +cp "$src/build/src/apprun/AppRun" "$here/appimage-build/AppRun/$version/AppRun-Release-$arch"