vt: avoid a VLA in the unicode screen scroll function
authorNicolas Pitre <nicolas.pitre@linaro.org>
Thu, 19 Jul 2018 04:05:25 +0000 (00:05 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 21 Jul 2018 07:19:46 +0000 (09:19 +0200)
The nr argument is typically small: most often nr == 1. However this
could be abused with a very large explicit scroll in a resized screen.
Make the code scroll lines by performing an array rotation operation to
avoid the need for a large temporary space.

Requested-by: Kees Cook <keescook@chromium.org>
Suggested-by: Adam Borowski <kilobyte@angband.pl>
Signed-off-by: Nicolas Pitre <nico@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/vt/vt.c

index 4b3c371f0f8495f24d5b1142109061a68481cc50..5f1183b0b89df3bc672ccf7299dc103dfddbf138 100644 (file)
 #include <linux/kdb.h>
 #include <linux/ctype.h>
 #include <linux/bsearch.h>
+#include <linux/gcd.h>
 
 #define MAX_NR_CON_DRIVER 16
 
@@ -434,20 +435,29 @@ static void vc_uniscr_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
        struct uni_screen *uniscr = get_vc_uniscr(vc);
 
        if (uniscr) {
-               unsigned int s, d, rescue, clear;
-               char32_t *save[nr];
-
-               s = clear = t;
-               d = t + nr;
-               rescue = b - nr;
-               if (dir == SM_UP) {
-                       swap(s, d);
-                       swap(clear, rescue);
+               unsigned int i, j, k, sz, d, clear;
+
+               sz = b - t;
+               clear = b - nr;
+               d = nr;
+               if (dir == SM_DOWN) {
+                       clear = t;
+                       d = sz - nr;
+               }
+               for (i = 0; i < gcd(d, sz); i++) {
+                       char32_t *tmp = uniscr->lines[t + i];
+                       j = i;
+                       while (1) {
+                               k = j + d;
+                               if (k >= sz)
+                                       k -= sz;
+                               if (k == i)
+                                       break;
+                               uniscr->lines[t + j] = uniscr->lines[t + k];
+                               j = k;
+                       }
+                       uniscr->lines[t + j] = tmp;
                }
-               memcpy(save, uniscr->lines + rescue, nr * sizeof(*save));
-               memmove(uniscr->lines + d, uniscr->lines + s,
-                       (b - t - nr) * sizeof(*uniscr->lines));
-               memcpy(uniscr->lines + clear, save, nr * sizeof(*save));
                vc_uniscr_clear_lines(vc, clear, nr);
        }
 }