From 5dbb1f6b5da941aee8dfde343f002c34d0efac05 Mon Sep 17 00:00:00 2001 From: Rick Morgans Date: Sat, 14 Mar 2026 22:49:55 +1030 Subject: [PATCH 1/3] fix: defensive checks for cwd restore, malloc, and log writes - socket_with_chdir: always restore cwd, even on socket failure - expand_sockname: check malloc return before use - rotate_log/pty_activity: disable logging on write failure instead of silently losing data Co-Authored-By: Claude Opus 4.6 (1M context) --- atch.c | 18 ++++++++++++++---- master.c | 10 ++++++++-- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/atch.c b/atch.c index ff8f2f1..c0a7c2b 100644 --- a/atch.c +++ b/atch.c @@ -76,11 +76,17 @@ int socket_with_chdir(char *path, int (*fn)(char *)) *slash = '\0'; s = chdir(path) >= 0 ? fn(slash + 1) : -1; *slash = '/'; - if (s >= 0 && fchdir(dirfd) < 0) { - close(s); - s = -1; + /* Always restore cwd, regardless of socket operation result */ + { + int saved_errno = errno; + if (fchdir(dirfd) < 0) { + if (s >= 0) close(s); + close(dirfd); + return -1; + } + close(dirfd); + errno = saved_errno; } - close(dirfd); return s; } @@ -275,6 +281,10 @@ static void expand_sockname(void) mkdir(dir, 0700); fulllen = strlen(dir) + 1 + strlen(sockname); full = malloc(fulllen + 1); + if (!full) { + printf("%s: out of memory\n", progname); + exit(1); + } snprintf(full, fulllen + 1, "%s/%s", dir, sockname); sockname = full; } diff --git a/master.c b/master.c index 16f29e3..0afaa09 100644 --- a/master.c +++ b/master.c @@ -67,7 +67,10 @@ static void rotate_log(void) if (n > 0) { ftruncate(log_fd, 0); lseek(log_fd, 0, SEEK_SET); - write(log_fd, buf, (size_t)n); + if (write(log_fd, buf, (size_t)n) < 0) { + close(log_fd); + log_fd = -1; + } } free(buf); } @@ -388,7 +391,10 @@ static void pty_activity(int s) } scrollback_append(buf, (size_t)len); if (log_fd >= 0) { - write(log_fd, buf, (size_t)len); + if (write(log_fd, buf, (size_t)len) < 0) { + close(log_fd); + log_fd = -1; + } log_written += (size_t)len; if (log_written >= log_max_size) { rotate_log(); From 4d8203473878569421de20ed2e9e99230e4f8189 Mon Sep 17 00:00:00 2001 From: Rick Morgans Date: Sat, 14 Mar 2026 23:29:33 +1030 Subject: [PATCH 2/3] fix: guard all log operations behind log_fd >= 0 Once logging is disabled (log_fd = -1), no code path should increment log_written, call rotate_log, or lseek/write the fd. Co-Authored-By: Claude Opus 4.6 (1M context) --- master.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/master.c b/master.c index 0afaa09..20a5971 100644 --- a/master.c +++ b/master.c @@ -58,6 +58,9 @@ static void rotate_log(void) char *buf; ssize_t n; + if (log_fd < 0) + return; + size = lseek(log_fd, 0, SEEK_END); if (size > (off_t) log_max_size) { buf = malloc(log_max_size); @@ -70,12 +73,15 @@ static void rotate_log(void) if (write(log_fd, buf, (size_t)n) < 0) { close(log_fd); log_fd = -1; + free(buf); + return; } } free(buf); } } - lseek(log_fd, 0, SEEK_END); + if (log_fd >= 0) + lseek(log_fd, 0, SEEK_END); } /* @@ -92,7 +98,7 @@ static int open_log(const char *path) log_fd = fd; rotate_log(); - return fd; + return log_fd; } /* Write end marker to log, close it, and unlink the socket. */ @@ -395,6 +401,8 @@ static void pty_activity(int s) close(log_fd); log_fd = -1; } + } + if (log_fd >= 0) { log_written += (size_t)len; if (log_written >= log_max_size) { rotate_log(); From 6d5568f01616b7f6188fa11a4d532fa861426e5d Mon Sep 17 00:00:00 2001 From: Rick Morgans Date: Sun, 15 Mar 2026 11:50:39 +1030 Subject: [PATCH 3/3] test: add cwd preservation regression test for socket_with_chdir Verifies that a failed socket operation via socket_with_chdir does not leave the process in the wrong working directory. Co-Authored-By: Claude Opus 4.6 (1M context) --- tests/test.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/test.sh b/tests/test.sh index 0c954c0..0c6fa14 100644 --- a/tests/test.sh +++ b/tests/test.sh @@ -720,6 +720,21 @@ assert_contains "no args: shows Usage:" "Usage:" "$out" run "$ATCH" --help assert_contains "help: shows tail command" "tail" "$out" +# ── 23. cwd preserved after socket failure ───────────────────────────────── +# socket_with_chdir must restore cwd even when the socket operation fails. +# We create the parent dir so chdir succeeds, but the session path is bogus. + +ORIG_PWD=$(pwd) +mkdir -p "$TESTDIR/sockdir" +"$ATCH" kill "$TESTDIR/sockdir/bogus" >/dev/null 2>&1 +AFTER_PWD=$(pwd) +if [ "$ORIG_PWD" = "$AFTER_PWD" ]; then + ok "cwd: preserved after failed socket operation" +else + fail "cwd: preserved after failed socket operation" "$ORIG_PWD" "$AFTER_PWD" + cd "$ORIG_PWD" +fi + # ── summary ────────────────────────────────────────────────────────────────── printf "\n1..%d\n" "$T"