File xterm-CVE-2023-40359.patch of Package xterm
From 41ba5cf31da5e43477811b28009d64d3f643fd29 Mon Sep 17 00:00:00 2001
From: "Thomas E. Dickey" <dickey@invisible-island.net>
Date: Wed, 8 Mar 2023 01:06:03 +0000
Subject: [PATCH] snapshot of project "xterm", label xterm-379c
---
MANIFEST | 2 +-
charclass.c | 4 +-
charproc.c | 44 +++++----
graphics_regis.c | 235 ++++++++++++++++++++++++++---------------------
xterm.log.html | 14 ++-
5 files changed, 171 insertions(+), 128 deletions(-)
Index: xterm-330/graphics_regis.c
===================================================================
--- xterm-330.orig/graphics_regis.c
+++ xterm-330/graphics_regis.c
@@ -146,6 +146,14 @@ typedef struct RegisTextControls {
int slant; /* for italic/oblique */
} RegisTextControls;
+#define S_QUOTE '\''
+#define D_QUOTE '"'
+
+#define isQuote(ch) ((ch) == S_QUOTE || (ch) == D_QUOTE)
+#define PickQuote(ch) ((ch) == S_QUOTE ? D_QUOTE : S_QUOTE)
+
+#define isName(c) ((c) == '_' || isalnum(CharOf(c)))
+
#define FixedCopy(dst, src, len) strncpy(dst, src, len - 1)[len - 1] = '\0'
#define CopyFontname(dst, src) FixedCopy(dst, src, REGIS_FONTNAME_LEN)
@@ -2956,6 +2964,37 @@ extract_regis_command(RegisDataFragment
return 1;
}
+/*
+ * Check a ReGIS alphabet name before reporting it, to pick an appropriate
+ * delimiter. If the string is empty, or contains nonreportable characters,
+ * just return NUL.
+ */
+static int
+pick_quote(const char *value)
+{
+ Bool s_quote = False;
+ Bool d_quote = False;
+
+ if (*value != '\0') {
+ while (*value != '\0') {
+ int ch = CharOf(*value++);
+ if (ch == D_QUOTE)
+ d_quote = True;
+ else if (ch == S_QUOTE)
+ s_quote = True;
+ else if (!isName(ch))
+ s_quote = d_quote = True;
+ }
+ } else {
+ s_quote = d_quote = True;
+ }
+ return ((s_quote && d_quote)
+ ? 0
+ : (s_quote
+ ? D_QUOTE
+ : S_QUOTE));
+}
+
static int
extract_regis_string(RegisDataFragment *input, char *out, unsigned maxlen)
{
@@ -2971,7 +3010,7 @@ extract_regis_string(RegisDataFragment *
return 0;
ch = peek_fragment(input);
- if (ch != '\'' && ch != '"')
+ if (!isQuote(ch))
return 0;
open_quote_ch = ch;
outlen = 0U;
@@ -3051,7 +3090,7 @@ extract_regis_parenthesized_data(RegisDa
for (; input->pos < input->len; input->pos++, output->len++) {
char prev_ch = ch;
ch = input->start[input->pos];
- if (ch == '\'' || ch == '"') {
+ if (isQuote(ch)) {
if (open_quote_ch == '\0') {
open_quote_ch = ch;
} else {
@@ -3119,7 +3158,7 @@ extract_regis_option(RegisDataFragment *
if (ch == ';' || ch == ',' ||
ch == '(' || ch == ')' ||
ch == '[' || ch == ']' ||
- ch == '"' || ch == '\'' ||
+ isQuote(ch) ||
isdigit(CharOf(ch))) {
return 0;
}
@@ -3135,7 +3174,7 @@ extract_regis_option(RegisDataFragment *
TRACE(("looking at char '%c' in option '%c'\n", ch, *option));
/* FIXME: any special rules for commas? */
/* FIXME: handle escaped quotes */
- if (ch == '\'' || ch == '"') {
+ if (isQuote(ch)) {
if (open_quote_ch == ch) {
open_quote_ch = '\0';
} else {
@@ -4821,6 +4860,7 @@ parse_regis_command(RegisParseState *sta
static int
parse_regis_option(RegisParseState *state, RegisGraphicsContext *context)
{
+ XtermWidget xw = context->display_graphic->xw;
RegisDataFragment optionarg;
if (!extract_regis_option(&state->input, &state->option, &optionarg))
@@ -5399,13 +5439,18 @@ parse_regis_option(RegisParseState *stat
state->option, fragment_to_tempstr(&optionarg)));
break;
} {
- char reply[64];
+ unsigned err_code = 0U;
+ unsigned err_char = 0U;
TRACE(("got report last error condition\n"));
/* FIXME: implement after adding error tracking */
- sprintf(reply, "\"%u,%u\"\r", 0U, 0U);
- unparseputs(context->display_graphic->xw, reply);
- unparse_end(context->display_graphic->xw);
+ unparseputc(xw, D_QUOTE);
+ unparseputn(xw, err_code);
+ unparseputc(xw, ',');
+ unparseputn(xw, err_char);
+ unparseputc(xw, D_QUOTE);
+ unparseputc(xw, '\r');
+ unparse_end(xw);
}
break;
case 'I':
@@ -5452,8 +5497,8 @@ parse_regis_option(RegisParseState *stat
/* FIXME: implement arrow key movement */
/* FIXME: implement button/key collection */
- unparseputs(context->display_graphic->xw, "\r");
- unparse_end(context->display_graphic->xw);
+ unparseputc(xw, '\r');
+ unparse_end(xw);
skip_regis_whitespace(&optionarg);
if (!fragment_consumed(&optionarg)) {
@@ -5470,25 +5515,22 @@ parse_regis_option(RegisParseState *stat
if (!fragment_consumed(&optionarg)) {
TRACE(("DATA_ERROR: unexpected arguments to ReGIS report command option '%c' arg \"%s\"\n",
state->option, fragment_to_tempstr(&optionarg)));
- break;
- } {
- char buffer[32];
-
- if (state->load_index == MAX_REGIS_ALPHABETS) {
- /* If this happens something went wrong elsewhere. */
- TRACE(("DATA_ERROR: unable to report current load alphabet\n"));
- unparseputs(context->display_graphic->xw, "A0\"\"\r");
- unparse_end(context->display_graphic->xw);
- break;
+ } else if (state->load_index == MAX_REGIS_ALPHABETS) {
+ /* If this happens something went wrong elsewhere. */
+ TRACE(("DATA_ERROR: unable to report current load alphabet\n"));
+ unparseputs(xw, "A0\"\"\r");
+ unparse_end(xw);
+ } else {
+ int delim = pick_quote(state->load_name);
+ if (delim != '\0') {
+ unparseputs(xw, "A");
+ unparseputn(xw, state->load_alphabet);
+ unparseputc(xw, delim);
+ unparseputs(xw, state->load_name);
+ unparseputc(xw, delim);
}
-
- unparseputs(context->display_graphic->xw, "A");
- sprintf(buffer, "%u", state->load_alphabet);
- unparseputs(context->display_graphic->xw, buffer);
- unparseputs(context->display_graphic->xw, "\"");
- unparseputs(context->display_graphic->xw, state->load_name);
- unparseputs(context->display_graphic->xw, "\"\r");
- unparse_end(context->display_graphic->xw);
+ unparseputc(xw, '\r');
+ unparse_end(xw);
}
break;
case 'M':
@@ -5530,27 +5572,31 @@ parse_regis_option(RegisParseState *stat
}
if (name == '=') {
- char reply[64];
+ unsigned max_available = 1000U;
+ unsigned cur_available = max_available;
TRACE(("got report macrograph storage request\n"));
/* FIXME: Implement when macrographs are supported. */
- sprintf(reply, "\"%u,%u\"\r", 1000U, 1000U);
- unparseputs(context->display_graphic->xw, reply);
- unparse_end(context->display_graphic->xw);
+ unparseputc(xw, D_QUOTE);
+ unparseputn(xw, cur_available);
+ unparseputc(xw, ',');
+ unparseputn(xw, max_available);
+ unparseputc(xw, D_QUOTE);
+ unparseputc(xw, '\r');
+ unparse_end(xw);
} else if (name < 'A' || name > 'Z') {
TRACE(("DATA_ERROR: invalid macrograph name: \"%c\"\n", name));
/* FIXME: what should happen? */
break;
} else {
- char temp[8];
-
TRACE(("got report macrograph request for name '%c'\n", name));
- sprintf(temp, "@=%c", name);
- unparseputs(context->display_graphic->xw, temp);
+ unparseputs(xw, "@=");
+ unparseputc(xw, name);
/* FIXME: Allow this to be disabled for security reasons. */
/* FIXME: implement when macrographs are supported. */
- unparseputs(context->display_graphic->xw, "@;\r");
- unparse_end(context->display_graphic->xw);
+ unparseputs(xw, "@;");
+ unparseputc(xw, '\r');
+ unparse_end(xw);
}
}
break;
@@ -5598,75 +5644,60 @@ parse_regis_option(RegisParseState *stat
TRACE(("got report cursor position (output=%d)\n", output));
/* FIXME: look into supporting ANSI locator reports (DECLRP) */
+ unparseputc(xw, L_BLOK);
if (output == 1) {
- char reply[64];
+ /* FIXME: verify in absolute, not user, coordinates */
+ unparseputn(xw, (unsigned) context->graphics_output_cursor_x);
+ unparseputc(xw, ',');
+ unparseputn(xw, (unsigned) context->graphics_output_cursor_y);
+ } else if (context->multi_input_mode) {
+ /* FIXME: track input coordinates */
+ unsigned x = 0, y = 0; /* placeholders */
+ /* send CSI240~[x,y]\r with current input cursor location */
+
+ /* FIXME: verify no leading char or button sequence */
+ /* FIXME: should we ever send an eight-bit CSI? */
/* FIXME: verify in absolute, not user, coordinates */
- sprintf(reply, "[%d,%d]\r",
- context->graphics_output_cursor_x,
- context->graphics_output_cursor_y);
- unparseputs(context->display_graphic->xw, reply);
- unparse_end(context->display_graphic->xw);
+ TRACE(("sending multi-mode input report at %u,%u\n", x, y));
+ unparseputn(xw, x);
+ unparseputc(xw, ',');
+ unparseputn(xw, y);
} else {
- char reply[64];
- int x, y;
+ char ch = ' '; /* placeholder */
+ unsigned x = 0, y = 0; /* placeholders */
- if (context->multi_input_mode) {
- /* FIXME: track input coordinates */
- x = y = 0; /* placeholders */
-
- /* send CSI240~[x,y]\r with current input cursor location */
-
- /* FIXME: verify no leading char or button sequence */
- /* FIXME: should we ever send an eight-bit CSI? */
- /* FIXME: verify in absolute, not user, coordinates */
- TRACE(("sending multi-mode input report at %d,%d\n",
- x, y));
- sprintf(reply, "[%d,%d]\r", x, y);
- unparseputs(context->display_graphic->xw, reply);
- unparse_end(context->display_graphic->xw);
- break;
- } else {
- char ch;
+ /* FIXME: wait for first non-arrow keypress or mouse click, and don't update graphics while waiting */
+ /* send <key or button>[x,y]\r to report input cursor location */
- /* FIXME: wait for first non-arrow keypress or mouse click, and don't update graphics while waiting */
- ch = ' '; /* placeholder */
- x = y = 0; /* placeholders */
-
- /* send <key or button>[x,y]\r to report input cursor location */
-
- /* null button: CSI240~ */
- /* left button: CSI241~ */
- /* middle button: CSI243~ */
- /* right button: CSI245~ */
- /* extra button: CSI247~ */
- /* FIXME: support DECLBD to change button assignments */
- /* FIXME: verify no leading char or button sequence */
- TRACE(("sending one-shot input report with %c at %d,%d\n",
- ch, x, y));
- if (ch == '\r') {
- /* Return only reports the location. */
- sprintf(reply, "[%d,%d]\r", x, y);
- } else if (ch == '\177') {
- /* DEL exits locator mode reporting nothing. */
- sprintf(reply, "\r");
- } else {
- sprintf(reply, "%c[%d,%d]\r", ch, x, y);
- }
- unparseputs(context->display_graphic->xw, reply);
- unparse_end(context->display_graphic->xw);
- /* FIXME: exit one-shot mode and disable input cursor */
- break;
- }
- }
+ /* null button: CSI240~ */
+ /* left button: CSI241~ */
+ /* middle button: CSI243~ */
+ /* right button: CSI245~ */
+ /* extra button: CSI247~ */
+ /* FIXME: support DECLBD to change button assignments */
+ /* FIXME: verify no leading char or button sequence */
+ TRACE(("sending one-shot input report with %c at %u,%u\n",
+ ch, x, y));
+ if (ch != '\177') {
+ unparseputn(xw, x);
+ unparseputc(xw, ',');
+ unparseputn(xw, y);
+ }
+ /* FIXME: exit one-shot mode and disable input cursor */
+ }
+ unparseputc(xw, R_BLOK);
+ unparseputc(xw, '\r');
+ unparse_end(xw);
}
break;
default:
TRACE(("DATA_ERROR: sending empty report for unknown ReGIS report command option '%c' arg \"%s\"\n",
state->option, fragment_to_tempstr(&optionarg)));
/* Unknown report request types must receive empty reports. */
- unparseputs(context->display_graphic->xw, "\r");
- unparse_end(context->display_graphic->xw);
+ unparseputs(xw, "\r");
+ unparse_end(xw);
+
break;
}
break;
@@ -5964,7 +5995,7 @@ parse_regis_option(RegisParseState *stat
TRACE(("using display page number: %d\n", page));
context->display_page = (unsigned) page;
- map_regis_graphics_pages(context->display_graphic->xw, context);
+ map_regis_graphics_pages(xw, context);
}
break;
case 'T':