new file mode 100644
@@ -0,0 +1,228 @@
+From d0b42b902bb6ca8a0a1393799db487642d3bf8ae Mon Sep 17 00:00:00 2001
+From: Nanang Izzuddin <nanang@teluu.com>
+Date: Wed, 17 Jun 2026 15:55:53 +0700
+Subject: [PATCH] Merge commit from fork
+
+* Fix pre-auth stack overflow formatting telnet CLI error indicator
+
+send_err_arg() in cli_telnet.c rendered the error caret into a fixed
+256-byte stack buffer using unchecked pj_strcat2; info->err_pos is
+bounded only by PJ_CLI_MAX_CMDBUF, so a long command line overflowed
+the buffer. Stream the spaces in bounded chunks via telnet_sess_send2,
+clamp the caret offset to PJ_CLI_MAX_CMDBUF, and send the message,
+prompt and echoed command separately, removing the stack buffer.
+
+Co-Authored-By Claude Code
+
+* Apply the same overflow fix to send_ambi_arg()
+
+Per review, send_ambi_arg() has the same issue as send_err_arg(): it
+renders an attacker-influenced number of caret spaces and appends the
+last command into a fixed 1028-byte stack buffer with unchecked
+pj_strcat2. info->err_pos and sess->rcmd->rbuf are bounded only by
+PJ_CLI_MAX_CMDBUF, so a long ambiguous command line could overflow it.
+
+Stream the caret offset in bounded chunks (clamped to PJ_CLI_MAX_CMDBUF)
+and stream the clamped last command separately, mirroring the
+send_err_arg() fix, instead of writing them into the fixed buffer. The
+shared SPACES constant is hoisted to file scope.
+
+Co-Authored-By Claude Code
+
+* Bound completion append in handle_tab()
+
+handle_tab() appended the matched command name into the fixed-size
+sess->rcmd->rbuf at offset len (the current command length) without
+checking that len + name length fits the buffer. len is bounded by the
+receive path, but the completion suffix is added on top, so a long
+completable command line could write past rbuf and its NUL terminator.
+
+Clamp the appended length to the space remaining in rbuf, leaving room
+for the terminator. Found while auditing the telnet CLI for the same
+fixed-buffer overflow pattern as send_err_arg()/send_ambi_arg().
+
+Co-Authored-By Claude Code
+
+(cherry picked from commit 4472a31d77a7506ff175dad5abef490f4e31bed1)
+
+CVE: CVE-2026-57166
+Upstream-Status: Backport [https://github.com/pjsip/pjproject/commit/4472a31d77a7506ff175dad5abef490f4e31bed1]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ pjlib-util/src/pjlib-util/cli_telnet.c | 116 ++++++++++++++++++-------
+ 1 file changed, 84 insertions(+), 32 deletions(-)
+
+diff --git a/pjlib-util/src/pjlib-util/cli_telnet.c b/pjlib-util/src/pjlib-util/cli_telnet.c
+index 0a2954bd4..67898fdd8 100644
+--- a/pjlib-util/src/pjlib-util/cli_telnet.c
++++ b/pjlib-util/src/pjlib-util/cli_telnet.c
+@@ -680,6 +680,13 @@ static void send_prompt_str(cli_telnet_sess *sess)
+ telnet_sess_send(sess, &send_data);
+ }
+
++/* Buffer of spaces, used to stream the caret offset in bounded chunks
++ * instead of writing an attacker-influenced number of spaces into a
++ * fixed-size stack buffer. Shared by send_err_arg() and send_ambi_arg().
++ */
++static const unsigned char SPACES[64] =
++ " ";
++
+ /*
+ * This method is used to send error message to client, including
+ * the error position of the source command.
+@@ -690,32 +697,45 @@ static void send_err_arg(cli_telnet_sess *sess,
+ pj_bool_t with_return,
+ pj_bool_t with_last_cmd)
+ {
+- pj_str_t send_data;
+- char data_str[256];
+- pj_size_t len;
+- unsigned i;
++ pj_size_t len, remaining;
+ cli_telnet_fe *fe = (cli_telnet_fe *)sess->base.fe;
+
+- send_data.ptr = data_str;
+- send_data.slen = 0;
+-
+ if (with_return)
+- pj_strcat2(&send_data, "\r\n");
++ telnet_sess_send2(sess, (const unsigned char*)"\r\n", 2);
+
++ /* Compute the caret offset. info->err_pos is derived from
++ * attacker-supplied input (pj_cli_sess_parse) and is bounded only by
++ * PJ_CLI_MAX_CMDBUF, so clamp it before emitting spaces. Otherwise a
++ * ~500-byte command line would overflow the fixed-size stack buffer
++ * this function previously used.
++ */
+ len = fe->cfg.prompt_str.slen + info->err_pos;
+-
+- /* Set the error pointer mark */
+- for (i=0;i<len;++i) {
+- pj_strcat2(&send_data, " ");
++ if (len > PJ_CLI_MAX_CMDBUF)
++ len = PJ_CLI_MAX_CMDBUF;
++
++ remaining = len;
++ while (remaining > 0) {
++ pj_size_t chunk = (remaining > sizeof(SPACES)) ? sizeof(SPACES)
++ : remaining;
++ telnet_sess_send2(sess, SPACES, (int)chunk);
++ remaining -= chunk;
+ }
+- pj_strcat2(&send_data, "^");
+- pj_strcat2(&send_data, "\r\n");
+- pj_strcat(&send_data, msg);
+- pj_strcat(&send_data, &fe->cfg.prompt_str);
+- if (with_last_cmd)
+- pj_strcat2(&send_data, (char *)sess->rcmd->rbuf);
+
+- telnet_sess_send(sess, &send_data);
++ telnet_sess_send2(sess, (const unsigned char*)"^\r\n", 3);
++
++ if (msg && msg->slen > 0)
++ telnet_sess_send(sess, msg);
++ if (fe->cfg.prompt_str.slen > 0)
++ telnet_sess_send(sess, &fe->cfg.prompt_str);
++ if (with_last_cmd) {
++ pj_str_t last;
++ last.ptr = (char *)sess->rcmd->rbuf;
++ last.slen = (pj_ssize_t)pj_ansi_strlen((const char *)sess->rcmd->rbuf);
++ if (last.slen > PJ_CLI_MAX_CMDBUF)
++ last.slen = PJ_CLI_MAX_CMDBUF;
++ if (last.slen > 0)
++ telnet_sess_send(sess, &last);
++ }
+ }
+
+ static void send_inv_arg(cli_telnet_sess *sess,
+@@ -765,7 +785,7 @@ static void send_ambi_arg(cli_telnet_sess *sess,
+ pj_bool_t with_last_cmd)
+ {
+ unsigned i;
+- pj_size_t len;
++ pj_size_t len, remaining;
+ pj_str_t send_data;
+ char data[1028];
+ cli_telnet_fe *fe = (cli_telnet_fe *)sess->base.fe;
+@@ -779,14 +799,25 @@ static void send_ambi_arg(cli_telnet_sess *sess,
+ send_data.slen = 0;
+
+ if (with_return)
+- pj_strcat2(&send_data, "\r\n");
++ telnet_sess_send2(sess, (const unsigned char*)"\r\n", 2);
+
++ /* Compute and clamp the caret offset, then stream the spaces, like
++ * send_err_arg(). info->err_pos is attacker-bounded only by
++ * PJ_CLI_MAX_CMDBUF, so it must not drive writes into data[].
++ */
+ len = fe->cfg.prompt_str.slen + info->err_pos;
+-
+- for (i=0;i<len;++i) {
+- pj_strcat2(&send_data, " ");
++ if (len > PJ_CLI_MAX_CMDBUF)
++ len = PJ_CLI_MAX_CMDBUF;
++
++ remaining = len;
++ while (remaining > 0) {
++ pj_size_t chunk = (remaining > sizeof(SPACES)) ? sizeof(SPACES)
++ : remaining;
++ telnet_sess_send2(sess, SPACES, (int)chunk);
++ remaining -= chunk;
+ }
+- pj_strcat2(&send_data, "^");
++ telnet_sess_send2(sess, (const unsigned char*)"^", 1);
++
+ /* Get the max length of the command name */
+ for (i=0;i<info->hint_cnt;++i) {
+ if (hint[i].type.slen > 0) {
+@@ -874,10 +905,21 @@ static void send_ambi_arg(cli_telnet_sess *sess,
+ }
+ pj_strcat2(&send_data, "\r\n");
+ pj_strcat(&send_data, &fe->cfg.prompt_str);
+- if (with_last_cmd)
+- pj_strcat2(&send_data, (char *)sess->rcmd->rbuf);
+-
+ telnet_sess_send(sess, &send_data);
++
++ /* Stream the last command separately with a clamp; sess->rcmd->rbuf
++ * is attacker-bounded only by PJ_CLI_MAX_CMDBUF, so appending it into
++ * data[] could overflow the fixed-size buffer.
++ */
++ if (with_last_cmd) {
++ pj_str_t last;
++ last.ptr = (char *)sess->rcmd->rbuf;
++ last.slen = (pj_ssize_t)pj_ansi_strlen((const char *)sess->rcmd->rbuf);
++ if (last.slen > PJ_CLI_MAX_CMDBUF)
++ last.slen = PJ_CLI_MAX_CMDBUF;
++ if (last.slen > 0)
++ telnet_sess_send(sess, &last);
++ }
+ }
+
+ /*
+@@ -1035,10 +1077,20 @@ static pj_bool_t handle_tab(cli_telnet_sess *sess)
+ }
+ send_comp_arg(sess, &info);
+
+- pj_memcpy(&sess->rcmd->rbuf[len], info.hint[0].name.ptr,
+- info.hint[0].name.slen);
+-
+- len += (unsigned)info.hint[0].name.slen;
++ /* Append the matched name into the fixed-size rbuf. len is
++ * attacker-influenced (the typed command length), so clamp
++ * the copy to leave room for the NUL terminator and avoid
++ * overflowing rbuf.
++ */
++ if (len < sizeof(sess->rcmd->rbuf) - 1) {
++ pj_size_t avail = sizeof(sess->rcmd->rbuf) - 1 - len;
++ pj_size_t cplen = (pj_size_t)info.hint[0].name.slen;
++ if (cplen > avail)
++ cplen = avail;
++ pj_memcpy(&sess->rcmd->rbuf[len],
++ info.hint[0].name.ptr, cplen);
++ len += (unsigned)cplen;
++ }
+ sess->rcmd->rbuf[len] = 0;
+ }
+ } else {
@@ -20,6 +20,7 @@ SRC_URI = "git://github.com/pjsip/pjproject.git;protocol=https;branch=master;tag
file://CVE-2026-57163.patch \
file://CVE-2026-57164.patch \
file://CVE-2026-57165.patch \
+ file://CVE-2026-57166.patch \
"
SRCREV = "5a457451fa2712ba18e12b01738e8ff3af2b26fd"