mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-10 01:48:00 +00:00
More robust control sequence parameter handling.
Parameters are now accumulated in unsigned integers and carefully checked for overflow (which is turned into saturation). Things that consume them now have explicit range checks (again, saturating) to ensure that their inputs are sane. This should make it much harder to cause overflow by supplying ludicrously large numbers. Fixes two bugs found with the help of afl-fuzz. One of them may be exploitable and is CVE-2015-5309.
This commit is contained in:
parent
f7365a2d57
commit
6056396f77
43
terminal.c
43
terminal.c
@ -5,6 +5,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@ -3493,8 +3494,15 @@ static void term_out(Terminal *term)
|
|||||||
if (term->esc_nargs <= ARGS_MAX) {
|
if (term->esc_nargs <= ARGS_MAX) {
|
||||||
if (term->esc_args[term->esc_nargs - 1] == ARG_DEFAULT)
|
if (term->esc_args[term->esc_nargs - 1] == ARG_DEFAULT)
|
||||||
term->esc_args[term->esc_nargs - 1] = 0;
|
term->esc_args[term->esc_nargs - 1] = 0;
|
||||||
|
if (term->esc_args[term->esc_nargs - 1] <=
|
||||||
|
UINT_MAX / 10 &&
|
||||||
|
term->esc_args[term->esc_nargs - 1] * 10 <=
|
||||||
|
UINT_MAX - c - '0')
|
||||||
term->esc_args[term->esc_nargs - 1] =
|
term->esc_args[term->esc_nargs - 1] =
|
||||||
10 * term->esc_args[term->esc_nargs - 1] + c - '0';
|
10 * term->esc_args[term->esc_nargs - 1] +
|
||||||
|
c - '0';
|
||||||
|
else
|
||||||
|
term->esc_args[term->esc_nargs - 1] = UINT_MAX;
|
||||||
}
|
}
|
||||||
term->termstate = SEEN_CSI;
|
term->termstate = SEEN_CSI;
|
||||||
} else if (c == ';') {
|
} else if (c == ';') {
|
||||||
@ -3510,8 +3518,10 @@ static void term_out(Terminal *term)
|
|||||||
term->esc_query = c;
|
term->esc_query = c;
|
||||||
term->termstate = SEEN_CSI;
|
term->termstate = SEEN_CSI;
|
||||||
} else
|
} else
|
||||||
|
#define CLAMP(arg, lim) ((arg) = ((arg) > (lim)) ? (lim) : (arg))
|
||||||
switch (ANSI(c, term->esc_query)) {
|
switch (ANSI(c, term->esc_query)) {
|
||||||
case 'A': /* CUU: move up N lines */
|
case 'A': /* CUU: move up N lines */
|
||||||
|
CLAMP(term->esc_args[0], term->rows);
|
||||||
move(term, term->curs.x,
|
move(term, term->curs.x,
|
||||||
term->curs.y - def(term->esc_args[0], 1), 1);
|
term->curs.y - def(term->esc_args[0], 1), 1);
|
||||||
seen_disp_event(term);
|
seen_disp_event(term);
|
||||||
@ -3520,6 +3530,7 @@ static void term_out(Terminal *term)
|
|||||||
compatibility(ANSI);
|
compatibility(ANSI);
|
||||||
/* FALLTHROUGH */
|
/* FALLTHROUGH */
|
||||||
case 'B': /* CUD: Cursor down */
|
case 'B': /* CUD: Cursor down */
|
||||||
|
CLAMP(term->esc_args[0], term->rows);
|
||||||
move(term, term->curs.x,
|
move(term, term->curs.x,
|
||||||
term->curs.y + def(term->esc_args[0], 1), 1);
|
term->curs.y + def(term->esc_args[0], 1), 1);
|
||||||
seen_disp_event(term);
|
seen_disp_event(term);
|
||||||
@ -3535,23 +3546,27 @@ static void term_out(Terminal *term)
|
|||||||
compatibility(ANSI);
|
compatibility(ANSI);
|
||||||
/* FALLTHROUGH */
|
/* FALLTHROUGH */
|
||||||
case 'C': /* CUF: Cursor right */
|
case 'C': /* CUF: Cursor right */
|
||||||
|
CLAMP(term->esc_args[0], term->cols);
|
||||||
move(term, term->curs.x + def(term->esc_args[0], 1),
|
move(term, term->curs.x + def(term->esc_args[0], 1),
|
||||||
term->curs.y, 1);
|
term->curs.y, 1);
|
||||||
seen_disp_event(term);
|
seen_disp_event(term);
|
||||||
break;
|
break;
|
||||||
case 'D': /* CUB: move left N cols */
|
case 'D': /* CUB: move left N cols */
|
||||||
|
CLAMP(term->esc_args[0], term->cols);
|
||||||
move(term, term->curs.x - def(term->esc_args[0], 1),
|
move(term, term->curs.x - def(term->esc_args[0], 1),
|
||||||
term->curs.y, 1);
|
term->curs.y, 1);
|
||||||
seen_disp_event(term);
|
seen_disp_event(term);
|
||||||
break;
|
break;
|
||||||
case 'E': /* CNL: move down N lines and CR */
|
case 'E': /* CNL: move down N lines and CR */
|
||||||
compatibility(ANSI);
|
compatibility(ANSI);
|
||||||
|
CLAMP(term->esc_args[0], term->rows);
|
||||||
move(term, 0,
|
move(term, 0,
|
||||||
term->curs.y + def(term->esc_args[0], 1), 1);
|
term->curs.y + def(term->esc_args[0], 1), 1);
|
||||||
seen_disp_event(term);
|
seen_disp_event(term);
|
||||||
break;
|
break;
|
||||||
case 'F': /* CPL: move up N lines and CR */
|
case 'F': /* CPL: move up N lines and CR */
|
||||||
compatibility(ANSI);
|
compatibility(ANSI);
|
||||||
|
CLAMP(term->esc_args[0], term->rows);
|
||||||
move(term, 0,
|
move(term, 0,
|
||||||
term->curs.y - def(term->esc_args[0], 1), 1);
|
term->curs.y - def(term->esc_args[0], 1), 1);
|
||||||
seen_disp_event(term);
|
seen_disp_event(term);
|
||||||
@ -3559,12 +3574,14 @@ static void term_out(Terminal *term)
|
|||||||
case 'G': /* CHA */
|
case 'G': /* CHA */
|
||||||
case '`': /* HPA: set horizontal posn */
|
case '`': /* HPA: set horizontal posn */
|
||||||
compatibility(ANSI);
|
compatibility(ANSI);
|
||||||
|
CLAMP(term->esc_args[0], term->cols);
|
||||||
move(term, def(term->esc_args[0], 1) - 1,
|
move(term, def(term->esc_args[0], 1) - 1,
|
||||||
term->curs.y, 0);
|
term->curs.y, 0);
|
||||||
seen_disp_event(term);
|
seen_disp_event(term);
|
||||||
break;
|
break;
|
||||||
case 'd': /* VPA: set vertical posn */
|
case 'd': /* VPA: set vertical posn */
|
||||||
compatibility(ANSI);
|
compatibility(ANSI);
|
||||||
|
CLAMP(term->esc_args[0], term->rows);
|
||||||
move(term, term->curs.x,
|
move(term, term->curs.x,
|
||||||
((term->dec_om ? term->marg_t : 0) +
|
((term->dec_om ? term->marg_t : 0) +
|
||||||
def(term->esc_args[0], 1) - 1),
|
def(term->esc_args[0], 1) - 1),
|
||||||
@ -3575,6 +3592,8 @@ static void term_out(Terminal *term)
|
|||||||
case 'f': /* HVP: set horz and vert posns at once */
|
case 'f': /* HVP: set horz and vert posns at once */
|
||||||
if (term->esc_nargs < 2)
|
if (term->esc_nargs < 2)
|
||||||
term->esc_args[1] = ARG_DEFAULT;
|
term->esc_args[1] = ARG_DEFAULT;
|
||||||
|
CLAMP(term->esc_args[0], term->rows);
|
||||||
|
CLAMP(term->esc_args[1], term->cols);
|
||||||
move(term, def(term->esc_args[1], 1) - 1,
|
move(term, def(term->esc_args[1], 1) - 1,
|
||||||
((term->dec_om ? term->marg_t : 0) +
|
((term->dec_om ? term->marg_t : 0) +
|
||||||
def(term->esc_args[0], 1) - 1),
|
def(term->esc_args[0], 1) - 1),
|
||||||
@ -3610,6 +3629,7 @@ static void term_out(Terminal *term)
|
|||||||
break;
|
break;
|
||||||
case 'L': /* IL: insert lines */
|
case 'L': /* IL: insert lines */
|
||||||
compatibility(VT102);
|
compatibility(VT102);
|
||||||
|
CLAMP(term->esc_args[0], term->rows);
|
||||||
if (term->curs.y <= term->marg_b)
|
if (term->curs.y <= term->marg_b)
|
||||||
scroll(term, term->curs.y, term->marg_b,
|
scroll(term, term->curs.y, term->marg_b,
|
||||||
-def(term->esc_args[0], 1), FALSE);
|
-def(term->esc_args[0], 1), FALSE);
|
||||||
@ -3617,6 +3637,7 @@ static void term_out(Terminal *term)
|
|||||||
break;
|
break;
|
||||||
case 'M': /* DL: delete lines */
|
case 'M': /* DL: delete lines */
|
||||||
compatibility(VT102);
|
compatibility(VT102);
|
||||||
|
CLAMP(term->esc_args[0], term->rows);
|
||||||
if (term->curs.y <= term->marg_b)
|
if (term->curs.y <= term->marg_b)
|
||||||
scroll(term, term->curs.y, term->marg_b,
|
scroll(term, term->curs.y, term->marg_b,
|
||||||
def(term->esc_args[0], 1),
|
def(term->esc_args[0], 1),
|
||||||
@ -3626,11 +3647,13 @@ static void term_out(Terminal *term)
|
|||||||
case '@': /* ICH: insert chars */
|
case '@': /* ICH: insert chars */
|
||||||
/* XXX VTTEST says this is vt220, vt510 manual says vt102 */
|
/* XXX VTTEST says this is vt220, vt510 manual says vt102 */
|
||||||
compatibility(VT102);
|
compatibility(VT102);
|
||||||
|
CLAMP(term->esc_args[0], term->cols);
|
||||||
insch(term, def(term->esc_args[0], 1));
|
insch(term, def(term->esc_args[0], 1));
|
||||||
seen_disp_event(term);
|
seen_disp_event(term);
|
||||||
break;
|
break;
|
||||||
case 'P': /* DCH: delete chars */
|
case 'P': /* DCH: delete chars */
|
||||||
compatibility(VT102);
|
compatibility(VT102);
|
||||||
|
CLAMP(term->esc_args[0], term->cols);
|
||||||
insch(term, -def(term->esc_args[0], 1));
|
insch(term, -def(term->esc_args[0], 1));
|
||||||
seen_disp_event(term);
|
seen_disp_event(term);
|
||||||
break;
|
break;
|
||||||
@ -3708,6 +3731,8 @@ static void term_out(Terminal *term)
|
|||||||
compatibility(VT100);
|
compatibility(VT100);
|
||||||
if (term->esc_nargs <= 2) {
|
if (term->esc_nargs <= 2) {
|
||||||
int top, bot;
|
int top, bot;
|
||||||
|
CLAMP(term->esc_args[0], term->rows);
|
||||||
|
CLAMP(term->esc_args[1], term->rows);
|
||||||
top = def(term->esc_args[0], 1) - 1;
|
top = def(term->esc_args[0], 1) - 1;
|
||||||
bot = (term->esc_nargs <= 1
|
bot = (term->esc_nargs <= 1
|
||||||
|| term->esc_args[1] == 0 ?
|
|| term->esc_args[1] == 0 ?
|
||||||
@ -4062,6 +4087,7 @@ static void term_out(Terminal *term)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'S': /* SU: Scroll up */
|
case 'S': /* SU: Scroll up */
|
||||||
|
CLAMP(term->esc_args[0], term->rows);
|
||||||
compatibility(SCOANSI);
|
compatibility(SCOANSI);
|
||||||
scroll(term, term->marg_t, term->marg_b,
|
scroll(term, term->marg_t, term->marg_b,
|
||||||
def(term->esc_args[0], 1), TRUE);
|
def(term->esc_args[0], 1), TRUE);
|
||||||
@ -4069,6 +4095,7 @@ static void term_out(Terminal *term)
|
|||||||
seen_disp_event(term);
|
seen_disp_event(term);
|
||||||
break;
|
break;
|
||||||
case 'T': /* SD: Scroll down */
|
case 'T': /* SD: Scroll down */
|
||||||
|
CLAMP(term->esc_args[0], term->rows);
|
||||||
compatibility(SCOANSI);
|
compatibility(SCOANSI);
|
||||||
scroll(term, term->marg_t, term->marg_b,
|
scroll(term, term->marg_t, term->marg_b,
|
||||||
-def(term->esc_args[0], 1), TRUE);
|
-def(term->esc_args[0], 1), TRUE);
|
||||||
@ -4111,6 +4138,7 @@ static void term_out(Terminal *term)
|
|||||||
/* XXX VTTEST says this is vt220, vt510 manual
|
/* XXX VTTEST says this is vt220, vt510 manual
|
||||||
* says vt100 */
|
* says vt100 */
|
||||||
compatibility(ANSIMIN);
|
compatibility(ANSIMIN);
|
||||||
|
CLAMP(term->esc_args[0], term->cols);
|
||||||
{
|
{
|
||||||
int n = def(term->esc_args[0], 1);
|
int n = def(term->esc_args[0], 1);
|
||||||
pos cursplus;
|
pos cursplus;
|
||||||
@ -4144,6 +4172,7 @@ static void term_out(Terminal *term)
|
|||||||
break;
|
break;
|
||||||
case 'Z': /* CBT */
|
case 'Z': /* CBT */
|
||||||
compatibility(OTHER);
|
compatibility(OTHER);
|
||||||
|
CLAMP(term->esc_args[0], term->cols);
|
||||||
{
|
{
|
||||||
int i = def(term->esc_args[0], 1);
|
int i = def(term->esc_args[0], 1);
|
||||||
pos old_curs = term->curs;
|
pos old_curs = term->curs;
|
||||||
@ -4204,7 +4233,7 @@ static void term_out(Terminal *term)
|
|||||||
break;
|
break;
|
||||||
case ANSI('F', '='): /* set normal foreground */
|
case ANSI('F', '='): /* set normal foreground */
|
||||||
compatibility(SCOANSI);
|
compatibility(SCOANSI);
|
||||||
if (term->esc_args[0] >= 0 && term->esc_args[0] < 16) {
|
if (term->esc_args[0] < 16) {
|
||||||
long colour =
|
long colour =
|
||||||
(sco2ansicolour[term->esc_args[0] & 0x7] |
|
(sco2ansicolour[term->esc_args[0] & 0x7] |
|
||||||
(term->esc_args[0] & 0x8)) <<
|
(term->esc_args[0] & 0x8)) <<
|
||||||
@ -4218,7 +4247,7 @@ static void term_out(Terminal *term)
|
|||||||
break;
|
break;
|
||||||
case ANSI('G', '='): /* set normal background */
|
case ANSI('G', '='): /* set normal background */
|
||||||
compatibility(SCOANSI);
|
compatibility(SCOANSI);
|
||||||
if (term->esc_args[0] >= 0 && term->esc_args[0] < 16) {
|
if (term->esc_args[0] < 16) {
|
||||||
long colour =
|
long colour =
|
||||||
(sco2ansicolour[term->esc_args[0] & 0x7] |
|
(sco2ansicolour[term->esc_args[0] & 0x7] |
|
||||||
(term->esc_args[0] & 0x8)) <<
|
(term->esc_args[0] & 0x8)) <<
|
||||||
@ -4342,7 +4371,11 @@ static void term_out(Terminal *term)
|
|||||||
case '7':
|
case '7':
|
||||||
case '8':
|
case '8':
|
||||||
case '9':
|
case '9':
|
||||||
|
if (term->esc_args[0] <= UINT_MAX / 10 &&
|
||||||
|
term->esc_args[0] * 10 <= UINT_MAX - c - '0')
|
||||||
term->esc_args[0] = 10 * term->esc_args[0] + c - '0';
|
term->esc_args[0] = 10 * term->esc_args[0] + c - '0';
|
||||||
|
else
|
||||||
|
term->esc_args[0] = UINT_MAX;
|
||||||
break;
|
break;
|
||||||
case 'L':
|
case 'L':
|
||||||
/*
|
/*
|
||||||
@ -4424,7 +4457,11 @@ static void term_out(Terminal *term)
|
|||||||
case '7':
|
case '7':
|
||||||
case '8':
|
case '8':
|
||||||
case '9':
|
case '9':
|
||||||
|
if (term->esc_args[0] <= UINT_MAX / 10 &&
|
||||||
|
term->esc_args[0] * 10 <= UINT_MAX - c - '0')
|
||||||
term->esc_args[0] = 10 * term->esc_args[0] + c - '0';
|
term->esc_args[0] = 10 * term->esc_args[0] + c - '0';
|
||||||
|
else
|
||||||
|
term->esc_args[0] = UINT_MAX;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
term->termstate = OSC_STRING;
|
term->termstate = OSC_STRING;
|
||||||
|
@ -172,7 +172,7 @@ struct terminal_tag {
|
|||||||
#define ARGS_MAX 32 /* max # of esc sequence arguments */
|
#define ARGS_MAX 32 /* max # of esc sequence arguments */
|
||||||
#define ARG_DEFAULT 0 /* if an arg isn't specified */
|
#define ARG_DEFAULT 0 /* if an arg isn't specified */
|
||||||
#define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
|
#define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
|
||||||
int esc_args[ARGS_MAX];
|
unsigned esc_args[ARGS_MAX];
|
||||||
int esc_nargs;
|
int esc_nargs;
|
||||||
int esc_query;
|
int esc_query;
|
||||||
#define ANSI(x,y) ((x)+((y)<<8))
|
#define ANSI(x,y) ((x)+((y)<<8))
|
||||||
|
Loading…
Reference in New Issue
Block a user