]> asedeno.scripts.mit.edu Git - PuTTY.git/commitdiff
More robust control sequence parameter handling.
authorBen Harris <bjh21@bjh21.me.uk>
Wed, 7 Oct 2015 22:54:39 +0000 (23:54 +0100)
committerBen Harris <bjh21@bjh21.me.uk>
Tue, 27 Oct 2015 19:59:14 +0000 (19:59 +0000)
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.

terminal.c
terminal.h

index d8d0ea0c38e8cfe2d8aa737487f4095a4bfa9c34..c7e2647e8971adbf08d039d83cd96f59c080c9a2 100644 (file)
@@ -5,6 +5,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
+#include <limits.h>
 
 #include <time.h>
 #include <assert.h>
@@ -3493,8 +3494,15 @@ static void term_out(Terminal *term)
                    if (term->esc_nargs <= ARGS_MAX) {
                        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] =
-                           10 * term->esc_args[term->esc_nargs - 1] + c - '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] =
+                               10 * term->esc_args[term->esc_nargs - 1] +
+                               c - '0';
+                       else
+                           term->esc_args[term->esc_nargs - 1] = UINT_MAX;
                    }
                    term->termstate = SEEN_CSI;
                } else if (c == ';') {
@@ -3510,8 +3518,10 @@ static void term_out(Terminal *term)
                        term->esc_query = c;
                    term->termstate = SEEN_CSI;
                } else
+#define CLAMP(arg, lim) ((arg) = ((arg) > (lim)) ? (lim) : (arg))
                    switch (ANSI(c, term->esc_query)) {
                      case 'A':       /* CUU: move up N lines */
+                       CLAMP(term->esc_args[0], term->rows);
                        move(term, term->curs.x,
                             term->curs.y - def(term->esc_args[0], 1), 1);
                        seen_disp_event(term);
@@ -3520,6 +3530,7 @@ static void term_out(Terminal *term)
                        compatibility(ANSI);
                        /* FALLTHROUGH */
                      case 'B':         /* CUD: Cursor down */
+                       CLAMP(term->esc_args[0], term->rows);
                        move(term, term->curs.x,
                             term->curs.y + def(term->esc_args[0], 1), 1);
                        seen_disp_event(term);
@@ -3535,23 +3546,27 @@ static void term_out(Terminal *term)
                        compatibility(ANSI);
                        /* FALLTHROUGH */
                      case 'C':         /* CUF: Cursor right */ 
+                       CLAMP(term->esc_args[0], term->cols);
                        move(term, term->curs.x + def(term->esc_args[0], 1),
                             term->curs.y, 1);
                        seen_disp_event(term);
                        break;
                      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),
                             term->curs.y, 1);
                        seen_disp_event(term);
                        break;
                      case 'E':       /* CNL: move down N lines and CR */
                        compatibility(ANSI);
+                       CLAMP(term->esc_args[0], term->rows);
                        move(term, 0,
                             term->curs.y + def(term->esc_args[0], 1), 1);
                        seen_disp_event(term);
                        break;
                      case 'F':       /* CPL: move up N lines and CR */
                        compatibility(ANSI);
+                       CLAMP(term->esc_args[0], term->rows);
                        move(term, 0,
                             term->curs.y - def(term->esc_args[0], 1), 1);
                        seen_disp_event(term);
@@ -3559,12 +3574,14 @@ static void term_out(Terminal *term)
                      case 'G':       /* CHA */
                      case '`':       /* HPA: set horizontal posn */
                        compatibility(ANSI);
+                       CLAMP(term->esc_args[0], term->cols);
                        move(term, def(term->esc_args[0], 1) - 1,
                             term->curs.y, 0);
                        seen_disp_event(term);
                        break;
                      case 'd':       /* VPA: set vertical posn */
                        compatibility(ANSI);
+                       CLAMP(term->esc_args[0], term->rows);
                        move(term, term->curs.x,
                             ((term->dec_om ? term->marg_t : 0) +
                              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 */
                        if (term->esc_nargs < 2)
                            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,
                             ((term->dec_om ? term->marg_t : 0) +
                              def(term->esc_args[0], 1) - 1),
@@ -3610,6 +3629,7 @@ static void term_out(Terminal *term)
                        break;
                      case 'L':       /* IL: insert lines */
                        compatibility(VT102);
+                       CLAMP(term->esc_args[0], term->rows);
                        if (term->curs.y <= term->marg_b)
                            scroll(term, term->curs.y, term->marg_b,
                                   -def(term->esc_args[0], 1), FALSE);
@@ -3617,6 +3637,7 @@ static void term_out(Terminal *term)
                        break;
                      case 'M':       /* DL: delete lines */
                        compatibility(VT102);
+                       CLAMP(term->esc_args[0], term->rows);
                        if (term->curs.y <= term->marg_b)
                            scroll(term, term->curs.y, term->marg_b,
                                   def(term->esc_args[0], 1),
@@ -3626,11 +3647,13 @@ static void term_out(Terminal *term)
                      case '@':       /* ICH: insert chars */
                        /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
                        compatibility(VT102);
+                       CLAMP(term->esc_args[0], term->cols);
                        insch(term, def(term->esc_args[0], 1));
                        seen_disp_event(term);
                        break;
                      case 'P':       /* DCH: delete chars */
                        compatibility(VT102);
+                       CLAMP(term->esc_args[0], term->cols);
                        insch(term, -def(term->esc_args[0], 1));
                        seen_disp_event(term);
                        break;
@@ -3708,6 +3731,8 @@ static void term_out(Terminal *term)
                        compatibility(VT100);
                        if (term->esc_nargs <= 2) {
                            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;
                            bot = (term->esc_nargs <= 1
                                   || term->esc_args[1] == 0 ?
@@ -4062,6 +4087,7 @@ static void term_out(Terminal *term)
                        }
                        break;
                      case 'S':         /* SU: Scroll up */
+                       CLAMP(term->esc_args[0], term->rows);
                        compatibility(SCOANSI);
                        scroll(term, term->marg_t, term->marg_b,
                               def(term->esc_args[0], 1), TRUE);
@@ -4069,6 +4095,7 @@ static void term_out(Terminal *term)
                        seen_disp_event(term);
                        break;
                      case 'T':         /* SD: Scroll down */
+                       CLAMP(term->esc_args[0], term->rows);
                        compatibility(SCOANSI);
                        scroll(term, term->marg_t, term->marg_b,
                               -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
                         * says vt100 */
                        compatibility(ANSIMIN);
+                       CLAMP(term->esc_args[0], term->cols);
                        {
                            int n = def(term->esc_args[0], 1);
                            pos cursplus;
@@ -4144,6 +4172,7 @@ static void term_out(Terminal *term)
                        break;
                      case 'Z':         /* CBT */
                        compatibility(OTHER);
+                       CLAMP(term->esc_args[0], term->cols);
                        {
                            int i = def(term->esc_args[0], 1);
                            pos old_curs = term->curs;
@@ -4204,7 +4233,7 @@ static void term_out(Terminal *term)
                        break;
                      case ANSI('F', '='):      /* set normal foreground */
                        compatibility(SCOANSI);
-                       if (term->esc_args[0] >= 0 && term->esc_args[0] < 16) {
+                       if (term->esc_args[0] < 16) {
                            long colour =
                                (sco2ansicolour[term->esc_args[0] & 0x7] |
                                 (term->esc_args[0] & 0x8)) <<
@@ -4218,7 +4247,7 @@ static void term_out(Terminal *term)
                        break;
                      case ANSI('G', '='):      /* set normal background */
                        compatibility(SCOANSI);
-                       if (term->esc_args[0] >= 0 && term->esc_args[0] < 16) {
+                       if (term->esc_args[0] < 16) {
                            long colour =
                                (sco2ansicolour[term->esc_args[0] & 0x7] |
                                 (term->esc_args[0] & 0x8)) <<
@@ -4342,7 +4371,11 @@ static void term_out(Terminal *term)
                  case '7':
                  case '8':
                  case '9':
-                   term->esc_args[0] = 10 * term->esc_args[0] + c - '0';
+                   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';
+                   else
+                       term->esc_args[0] = UINT_MAX;
                    break;
                  case 'L':
                    /*
@@ -4424,7 +4457,11 @@ static void term_out(Terminal *term)
                  case '7':
                  case '8':
                  case '9':
-                   term->esc_args[0] = 10 * term->esc_args[0] + c - '0';
+                   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';
+                   else
+                       term->esc_args[0] = UINT_MAX;
                    break;
                  default:
                    term->termstate = OSC_STRING;
index 135ef45a6ce9636f9e3f4d06055905bd952e7dc0..01d5f57a2d7e4f3f189f1ea7276eca6998c58cb6 100644 (file)
@@ -172,7 +172,7 @@ struct terminal_tag {
 #define ARGS_MAX 32                   /* max # of esc sequence arguments */
 #define ARG_DEFAULT 0                 /* if an arg isn't specified */
 #define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
-    int esc_args[ARGS_MAX];
+    unsigned esc_args[ARGS_MAX];
     int esc_nargs;
     int esc_query;
 #define ANSI(x,y)      ((x)+((y)<<8))