diff options
author | Nicolas Pitre <nicolas.pitre@linaro.org> | 2018-07-19 07:05:25 +0300 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-07-21 10:19:46 +0300 |
commit | d541ae4e76ae35b76490206ef946a9124b993e32 (patch) | |
tree | 5c04d74c91af1d1a312030921042f433a7e8942a /drivers/tty/vt/vt.c | |
parent | 13aa0a12183f804d95c4c5c4d09df19ea74eb424 (diff) | |
download | linux-d541ae4e76ae35b76490206ef946a9124b993e32.tar.xz |
vt: avoid a VLA in the unicode screen scroll function
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>
Diffstat (limited to 'drivers/tty/vt/vt.c')
-rw-r--r-- | drivers/tty/vt/vt.c | 36 |
1 files changed, 23 insertions, 13 deletions
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 4b3c371f0f84..5f1183b0b89d 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -104,6 +104,7 @@ #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); } } |