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 7412320f42071f90e04732981126843011683f37
Author: [email protected] <[email protected]>
AuthorDate: Wed Mar 18 22:53:27 2026 -0600

    fix: add cmsg_len validation for SCM_RIGHTS and improve tymux attach robustness
    
    Critical security fix: validate cmsg_len before dereferencing SCM_RIGHTS
    control message to prevent reading garbage fd from truncated messages.
    
    Additional improvements to the tymux attach inline terminal proxy:
    - Safe multiplication: cast before multiplying to prevent integer overflow
    - Handle Terminology-internal colour codes (COL_INVIS, COL_INVERSE,
      COL_INVERSEBG) by falling back to default ANSI colours
    - Respect invisible cell attribute when rendering glyphs
    - Validate session name length to prevent buffer overflows
    - Improve SIGINT handler documentation and user feedback
    
    Applies validation fix symmetrically in both tymux.c and termsrv.c.
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---
 src/bin/termsrv.c |  3 ++-
 src/bin/tymux.c   | 32 ++++++++++++++++++++++++--------
 2 files changed, 26 insertions(+), 9 deletions(-)

diff --git a/src/bin/termsrv.c b/src/bin/termsrv.c
index 6e113d05..6e85599b 100644
--- a/src/bin/termsrv.c
+++ b/src/bin/termsrv.c
@@ -225,7 +225,8 @@ termsrv_recv_attached(int sock_fd, int *shm_fd_out,
     if (hdr.type != TSRV_MSG_ATTACHED) return -1;
 
     struct cmsghdr *cm = CMSG_FIRSTHDR(&msg);
-    if (!cm || cm->cmsg_type != SCM_RIGHTS) return -1;
+    if (!cm || cm->cmsg_type != SCM_RIGHTS ||
+        cm->cmsg_len < CMSG_LEN(sizeof(int))) return -1;
     memcpy(shm_fd_out, CMSG_DATA(cm), sizeof(int));
 
     *cols_out = pl.cols;
diff --git a/src/bin/tymux.c b/src/bin/tymux.c
index 1242db9c..ead76108 100644
--- a/src/bin/tymux.c
+++ b/src/bin/tymux.c
@@ -316,7 +316,8 @@ _proto_recv_attached(int sock_fd, int *shm_fd_out,
    if (hdr.type != _TSRV_MSG_ATTACHED) return -1;
 
    struct cmsghdr *cm = CMSG_FIRSTHDR(&msg);
-   if (!cm || cm->cmsg_type != SCM_RIGHTS) return -1;
+   if (!cm || cm->cmsg_type != SCM_RIGHTS ||
+       cm->cmsg_len < CMSG_LEN(sizeof(int))) return -1;
    memcpy(shm_fd_out, CMSG_DATA(cm), sizeof(int));
 
    *cols_out = pl.cols;
@@ -461,7 +462,7 @@ _render_screen(const void *shm, uint32_t cols, uint32_t rows)
 
    /* Output buffer — worst case ~6 bytes/cell (UTF-8) + ~40 bytes ANSI/cell.
     * We heap-alloc to avoid large stack frames. */
-   size_t bufsz = (size_t)(scols * srows) * 50 + 256;
+   size_t bufsz = (size_t)scols * (size_t)srows * 50 + 256;
    char *out = malloc(bufsz);
    if (!out) return;
 
@@ -541,8 +542,11 @@ _render_screen(const void *shm, uint32_t cols, uint32_t rows)
                   else
                     {
                        /* Standard ANSI: 0=default fg, 1..8=colours 30..37,
-                        * fgintense shifts to 90..97 range. */
-                       if (c->att.fg == 0)
+                        * fgintense shifts to 90..97 range.
+                        * Values >8 (COL_INVIS=9, COL_INVERSE=10,
+                        * COL_INVERSEBG=11) are Terminology-internal and
+                        * have no ANSI mapping — fall back to default fg. */
+                       if (c->att.fg == 0 || c->att.fg > 8)
                          EMIT("\033[39m"); /* default fg */
                        else
                          {
@@ -565,7 +569,9 @@ _render_screen(const void *shm, uint32_t cols, uint32_t rows)
                     }
                   else
                     {
-                       if (c->att.bg == 0)
+                       /* Values >8 are Terminology-internal (COL_INVIS etc.)
+                        * with no ANSI mapping — fall back to default bg. */
+                       if (c->att.bg == 0 || c->att.bg > 8)
                          EMIT("\033[49m"); /* default bg */
                        else
                          {
@@ -577,9 +583,9 @@ _render_screen(const void *shm, uint32_t cols, uint32_t rows)
                   cur_bg256  = (int)c->att.bg256;
                }
 
-             /* Output the glyph (or space for empty / control cells) */
+             /* Output the glyph (or space for empty / control / invisible cells) */
              uint32_t cp = c->codepoint;
-             if (cp == 0 || cp < 0x20)
+             if (c->att.invisible || cp == 0 || cp < 0x20)
                {
                   if (pos < bufsz - 1) out[pos++] = ' ';
                }
@@ -810,6 +816,13 @@ cmd_attach(int argc, char **argv)
 
    const char *name = argv[2];
 
+   if (strlen(name) > TYMUX_MAX_SESSION_NAME)
+     {
+        fprintf(stderr, "tymux: session name too long (max %d chars)\n",
+                TYMUX_MAX_SESSION_NAME);
+        return 1;
+     }
+
    /* Warn if we are already inside a tymux session. */
    if (getenv("TYMUX_SESSION"))
      fprintf(stderr,
@@ -943,8 +956,11 @@ cmd_attach(int argc, char **argv)
 
         if (_got_int)
           {
-             /* User pressed Ctrl+C — detach cleanly. */
+             /* Received SIGINT (external kill) — detach cleanly.
+              * In raw mode, Ctrl+C sends 0x03 as data, not a signal;
+              * this handler fires only for signals from external processes. */
              _got_int = 0;
+             write(STDERR_FILENO, "\r\ntymux: received SIGINT, detaching\r\n", 37);
              _proto_send(sock, _TSRV_MSG_DETACH, NULL, 0);
              running = 0;
              break;

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to