Skip to content

Commit 949cb54

Browse files
committed
Add patch to fix file open crash with v3.4.3
Apply 9de8f6ab1f proposed by tridge upstream as part of RsyncProject/rsync#909 to fix a crash when attempting to open files.
1 parent c6e951d commit 949cb54

1 file changed

Lines changed: 104 additions & 0 deletions

File tree

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
From 9de8f6ab1f04cd61a7b2ef96facc7af55792bcfd Mon Sep 17 00:00:00 2001
2+
From: Andrew Tridgell <andrew@tridgell.net>
3+
Date: Fri, 22 May 2026 15:13:11 +1000
4+
Subject: [PATCH] syscall: survive a seccomp-blocked openat2() instead of dying
5+
on SIGSYS
6+
7+
secure_relative_open() invokes openat2(2) directly via syscall() (glibc
8+
lacked a wrapper for years). In a seccomp-restricted environment a
9+
disallowed syscall raises SIGSYS and kills the process rather than
10+
failing with ENOSYS, so the existing "fall back on ENOSYS" path never
11+
runs. The Android app sandbox blocks openat2 exactly this way: on Termux
12+
every rsync transfer died the moment the receiver opened a basis file
13+
(receiver.c calls secure_relative_open unconditionally), with the peer
14+
seeing only "connection unexpectedly closed". The same hazard applies to
15+
hardened containers and systemd's SystemCallFilter.
16+
17+
Probe openat2 once behind a temporary SIGSYS handler (sigsetjmp/
18+
siglongjmp). If it is missing or blocked, secure_relative_open_linux()
19+
returns ENOSYS so callers use the portable per-component O_NOFOLLOW walk,
20+
which relies only on openat() and is never seccomp-blocked. Where openat2
21+
is available (the normal case) behaviour is unchanged.
22+
23+
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
24+
---
25+
syscall.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
26+
1 file changed, 61 insertions(+)
27+
28+
diff --git a/syscall.c b/syscall.c
29+
index e317bccc3..61a086c3e 100644
30+
--- a/syscall.c
31+
+++ b/syscall.c
32+
@@ -1692,11 +1692,72 @@ static int path_has_dotdot_component(const char *path)
33+
}
34+
35+
#ifdef __linux__
36+
+#include <setjmp.h>
37+
+
38+
+/* openat2(2) is invoked directly via syscall() (glibc lacked a wrapper
39+
+ * for years). In a seccomp-restricted environment -- the Android app
40+
+ * sandbox, a hardened container, or systemd's SystemCallFilter -- a
41+
+ * disallowed syscall raises SIGSYS and kills the process rather than
42+
+ * failing with ENOSYS, so checking errno after the fact is too late.
43+
+ * Probe openat2 once behind a temporary SIGSYS handler; if it is missing
44+
+ * or blocked, secure_relative_open_linux() reports ENOSYS so the caller
45+
+ * falls back to the portable per-component O_NOFOLLOW walk. */
46+
+static sigjmp_buf openat2_probe_env;
47+
+
48+
+static void openat2_probe_handler(int signo)
49+
+{
50+
+ (void)signo;
51+
+ siglongjmp(openat2_probe_env, 1);
52+
+}
53+
+
54+
+static int openat2_usable(void)
55+
+{
56+
+ static int cached = -1;
57+
+ struct sigaction sa, old_sa;
58+
+
59+
+ if (cached >= 0)
60+
+ return cached;
61+
+
62+
+ memset(&sa, 0, sizeof sa);
63+
+ sa.sa_handler = openat2_probe_handler;
64+
+ sigemptyset(&sa.sa_mask);
65+
+ if (sigaction(SIGSYS, &sa, &old_sa) != 0)
66+
+ return cached = 0;
67+
+
68+
+ if (sigsetjmp(openat2_probe_env, 1) != 0) {
69+
+ /* SIGSYS delivered: openat2 is blocked by a seccomp filter. */
70+
+ cached = 0;
71+
+ } else {
72+
+ struct open_how how;
73+
+ int fd;
74+
+ memset(&how, 0, sizeof how);
75+
+ how.flags = O_RDONLY | O_DIRECTORY;
76+
+ how.resolve = RESOLVE_BENEATH | RESOLVE_NO_MAGICLINKS;
77+
+ fd = syscall(SYS_openat2, AT_FDCWD, ".", &how, sizeof how);
78+
+ if (fd >= 0) {
79+
+ close(fd);
80+
+ cached = 1;
81+
+ } else {
82+
+ /* ENOSYS = kernel too old; any other errno means the
83+
+ * syscall is wired up and reachable, so it is usable. */
84+
+ cached = errno != ENOSYS;
85+
+ }
86+
+ }
87+
+
88+
+ sigaction(SIGSYS, &old_sa, NULL);
89+
+ return cached;
90+
+}
91+
+
92+
static int secure_relative_open_linux(const char *basedir, const char *relpath, int flags, mode_t mode)
93+
{
94+
struct open_how how;
95+
int dirfd, retfd;
96+
97+
+ if (!openat2_usable()) {
98+
+ errno = ENOSYS;
99+
+ return -1;
100+
+ }
101+
+
102+
memset(&how, 0, sizeof how);
103+
how.flags = flags;
104+
how.mode = mode;

0 commit comments

Comments
 (0)