This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch tymux
in repository terminology.
View the commit online.
commit 146bc6cc60166a467eb5c3522cefd13f7e37c4e2
Author: [email protected] <[email protected]>
AuthorDate: Mon Mar 16 14:08:40 2026 -0600
fix: resolve critical issues in tymux code review
Remove O_NONBLOCK from tymuxd client fd, which was causing msg_recv to
fail with EAGAIN errors under blocking conditions. Add strict validation
for session names (reject empty names and paths with '/') before resource
allocation to prevent malformed socket creation.
Replace split write() calls with atomic sendmsg+MSG_NOSIGNAL for message
framing integrity. Add MSG_WAITALL to recvmsg in recv_attached and validate
message type range [1..9] to reject corrupt frames.
Implement double-fork in both termsession and tymux cmd_new() to prevent
zombie daemon processes when tymuxd exits. Store TSRV_MSG_TITLE payload
in shadow_pty->prop.title so window titles update correctly in session mode.
Register Ipc_Instance.session in Eet EDD so --session works via IPC
single-instance mode.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---
src/bin/ipc.c | 4 ++++
src/bin/termsession.c | 17 +++++++++++++++--
src/bin/termsrv.c | 18 +++++++++++++-----
src/bin/tymux.c | 19 ++++++++++++++++---
src/bin/tymuxd.c | 13 ++++---------
5 files changed, 52 insertions(+), 19 deletions(-)
diff --git a/src/bin/ipc.c b/src/bin/ipc.c
index 6cad2b9f..9384b944 100644
--- a/src/bin/ipc.c
+++ b/src/bin/ipc.c
@@ -116,6 +116,10 @@ ipc_init(void)
"font", font, EET_T_STRING);
EET_DATA_DESCRIPTOR_ADD_BASIC(new_inst_edd, Ipc_Instance,
"startup_id", startup_id, EET_T_STRING);
+#ifdef HAVE_TYMUX
+ EET_DATA_DESCRIPTOR_ADD_BASIC(new_inst_edd, Ipc_Instance,
+ "session", session, EET_T_STRING);
+#endif
EET_DATA_DESCRIPTOR_ADD_BASIC(new_inst_edd, Ipc_Instance,
"x", x, EET_T_INT);
EET_DATA_DESCRIPTOR_ADD_BASIC(new_inst_edd, Ipc_Instance,
diff --git a/src/bin/termsession.c b/src/bin/termsession.c
index 918a8e19..9235c645 100644
--- a/src/bin/termsession.c
+++ b/src/bin/termsession.c
@@ -14,6 +14,7 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/mman.h>
+#include <sys/wait.h>
#include <fcntl.h>
#include <stdatomic.h>
#include <time.h>
@@ -172,6 +173,8 @@ _cb_session_data(void *data, Ecore_Fd_Handler *handler EINA_UNUSED)
}
case TSRV_MSG_TITLE:
+ if (payload && size > 0)
+ eina_stringshare_replace(&ts->shadow_pty->prop.title, payload);
if (ts->cb_title.func)
ts->cb_title.func(ts->cb_title.data);
break;
@@ -224,11 +227,18 @@ _ensure_daemon(const char *session_name, const char *sock_path)
if (child == 0)
{
- /* Child: become session leader, redirect stdio, exec daemon */
+ /* First child: become session leader then double-fork so the
+ * daemon is reparented to init and leaves no zombie. */
+ pid_t gc;
int fd;
setsid();
+ gc = fork();
+ if (gc < 0) _exit(1);
+ if (gc > 0) _exit(0); /* first child exits; grandchild continues */
+
+ /* Grandchild: redirect stdio and exec daemon */
fd = open("/dev/null", O_RDWR);
if (fd >= 0)
{
@@ -244,7 +254,10 @@ _ensure_daemon(const char *session_name, const char *sock_path)
_exit(127);
}
- /* Parent: poll for socket with 10 ms intervals, 3 s total */
+ /* Parent: reap the short-lived first child immediately */
+ waitpid(child, NULL, 0);
+
+ /* Poll for socket with 10 ms intervals, 3 s total */
ts_sleep.tv_sec = 0;
ts_sleep.tv_nsec = 10 * 1000 * 1000; /* 10 ms */
diff --git a/src/bin/termsrv.c b/src/bin/termsrv.c
index 0f65e1de..deda63f8 100644
--- a/src/bin/termsrv.c
+++ b/src/bin/termsrv.c
@@ -73,10 +73,14 @@ termsrv_msg_send(int fd, TermSrvMsgType type,
const void *payload, uint32_t size)
{
TermSrvMsgHdr hdr = { .type = (uint32_t)type, .size = size };
- if (write(fd, &hdr, sizeof(hdr)) != (ssize_t)sizeof(hdr)) return -1;
- if (size && payload)
- if (write(fd, payload, size) != (ssize_t)size) return -1;
- return 0;
+ struct iovec iov[2] = {
+ { .iov_base = &hdr, .iov_len = sizeof(hdr) },
+ { .iov_base = (void *)payload, .iov_len = size },
+ };
+ int iovcnt = (size && payload) ? 2 : 1;
+ struct msghdr msg = { .msg_iov = iov, .msg_iovlen = (size_t)iovcnt };
+ ssize_t n = sendmsg(fd, &msg, MSG_NOSIGNAL);
+ return (n < 0) ? -1 : 0;
}
int
@@ -100,6 +104,9 @@ termsrv_msg_recv(int fd, void **payload_out, uint32_t *size_out)
{ free(buf); return -1; }
*payload_out = buf;
}
+ /* Reject unknown message types; valid range is ATTACH(1)..EXIT(9). */
+ if (hdr.type == 0 || hdr.type > 9) return -1;
+
*size_out = hdr.size;
return (int)hdr.type;
}
@@ -153,7 +160,8 @@ termsrv_recv_attached(int sock_fd, int *shm_fd_out,
};
*shm_fd_out = -1;
- if (recvmsg(sock_fd, &msg, 0) < 0) return -1;
+ ssize_t n = recvmsg(sock_fd, &msg, MSG_WAITALL);
+ if (n < (ssize_t)(sizeof(hdr) + sizeof(pl))) return -1;
if (msg.msg_flags & MSG_CTRUNC) return -1;
if (hdr.type != TSRV_MSG_ATTACHED) return -1;
diff --git a/src/bin/tymux.c b/src/bin/tymux.c
index ce485c64..440d14dc 100644
--- a/src/bin/tymux.c
+++ b/src/bin/tymux.c
@@ -21,6 +21,7 @@
#include <sys/un.h>
#include <fcntl.h>
#include <time.h>
+#include <sys/wait.h>
/* ── path helpers (mirrors termsrv.c, no EFL) ───────────────────────── */
@@ -167,7 +168,9 @@ cmd_new(int argc, char **argv)
return 1;
}
- /* Fork a detached tymuxd. */
+ /* Double-fork so tymuxd is reparented to init immediately and leaves
+ * no zombie in this process (or in terminology) when it eventually
+ * exits. */
pid_t pid = fork();
if (pid < 0)
{
@@ -177,9 +180,16 @@ cmd_new(int argc, char **argv)
if (pid == 0)
{
- /* Child: become session leader and redirect stdio. */
+ /* First child: become session leader then double-fork. */
+ pid_t gc;
+
setsid();
+ gc = fork();
+ if (gc < 0) _exit(1);
+ if (gc > 0) _exit(0); /* first child exits immediately */
+
+ /* Grandchild: redirect stdio and exec daemon. */
int devnull = open("/dev/null", O_RDWR);
if (devnull >= 0)
{
@@ -196,7 +206,10 @@ cmd_new(int argc, char **argv)
_exit(127);
}
- /* Parent: poll for the socket to appear (up to 3 s, 10 ms intervals). */
+ /* Parent: reap the short-lived first child immediately. */
+ waitpid(pid, NULL, 0);
+
+ /* Poll for the socket to appear (up to 3 s, 10 ms intervals). */
struct timespec ts = { 0, 10 * 1000 * 1000 }; /* 10 ms */
int attempts = 300; /* 3 s total */
int ready = 0;
diff --git a/src/bin/tymuxd.c b/src/bin/tymuxd.c
index 2d777152..97c25928 100644
--- a/src/bin/tymuxd.c
+++ b/src/bin/tymuxd.c
@@ -7,7 +7,6 @@
#include <Eina.h>
#include <errno.h>
-#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
@@ -46,11 +45,6 @@ _cb_client_connect(void *data EINA_UNUSED,
return ECORE_CALLBACK_RENEW;
}
- {
- int flags = fcntl(cfd, F_GETFL, 0);
- if (flags >= 0) fcntl(cfd, F_SETFL, flags | O_NONBLOCK);
- }
-
type = termsrv_msg_recv(cfd, &payload, &size);
if (type != TSRV_MSG_ATTACH)
{
@@ -85,11 +79,12 @@ main(int argc, char **argv)
name = argv[1];
- if (strlen(name) > TSRV_MAX_SESSION_NAME)
+ if (name[0] == '\0' || strlen(name) > TSRV_MAX_SESSION_NAME ||
+ strchr(name, '/'))
{
fprintf(stderr,
- "tymuxd: session name too long (max %d characters)\n",
- TSRV_MAX_SESSION_NAME);
+ "tymuxd: invalid session name '%s' "
+ "(empty, too long, or contains '/')\n", name);
return 1;
}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.