Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

vt: keyboard: avoid signed integer overflow in k_ascii

When k_ascii is invoked several times in a row there is a potential for
signed integer overflow:

UBSAN: Undefined behaviour in drivers/tty/vt/keyboard.c:888:19 signed integer overflow:
10 * 1111111111 cannot be represented in type 'int'
CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.6.11 #1
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
Call Trace:
<IRQ>
__dump_stack lib/dump_stack.c:77 [inline]
dump_stack+0xce/0x128 lib/dump_stack.c:118
ubsan_epilogue+0xe/0x30 lib/ubsan.c:154
handle_overflow+0xdc/0xf0 lib/ubsan.c:184
__ubsan_handle_mul_overflow+0x2a/0x40 lib/ubsan.c:205
k_ascii+0xbf/0xd0 drivers/tty/vt/keyboard.c:888
kbd_keycode drivers/tty/vt/keyboard.c:1477 [inline]
kbd_event+0x888/0x3be0 drivers/tty/vt/keyboard.c:1495

While it can be worked around by using check_mul_overflow()/
check_add_overflow(), it is better to introduce a separate flag to
signal that number pad is being used to compose a symbol, and
change type of the accumulator from signed to unsigned, thus
avoiding undefined behavior when it overflows.

Reported-by: Kyungtae Kim <kt0755@gmail.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: stable <stable@vger.kernel.org>
Link: https://lore.kernel.org/r/20200525232740.GA262061@dtor-ws
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Dmitry Torokhov and committed by
Greg Kroah-Hartman
b86dab05 15a3f03d

+16 -10
+16 -10
drivers/tty/vt/keyboard.c
··· 127 127 static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */ 128 128 static unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */ 129 129 static bool dead_key_next; 130 - static int npadch = -1; /* -1 or number assembled on pad */ 130 + 131 + /* Handles a number being assembled on the number pad */ 132 + static bool npadch_active; 133 + static unsigned int npadch_value; 134 + 131 135 static unsigned int diacr; 132 136 static char rep; /* flag telling character repeat */ 133 137 ··· 849 845 shift_state &= ~(1 << value); 850 846 851 847 /* kludge */ 852 - if (up_flag && shift_state != old_state && npadch != -1) { 848 + if (up_flag && shift_state != old_state && npadch_active) { 853 849 if (kbd->kbdmode == VC_UNICODE) 854 - to_utf8(vc, npadch); 850 + to_utf8(vc, npadch_value); 855 851 else 856 - put_queue(vc, npadch & 0xff); 857 - npadch = -1; 852 + put_queue(vc, npadch_value & 0xff); 853 + npadch_active = false; 858 854 } 859 855 } 860 856 ··· 872 868 873 869 static void k_ascii(struct vc_data *vc, unsigned char value, char up_flag) 874 870 { 875 - int base; 871 + unsigned int base; 876 872 877 873 if (up_flag) 878 874 return; ··· 886 882 base = 16; 887 883 } 888 884 889 - if (npadch == -1) 890 - npadch = value; 891 - else 892 - npadch = npadch * base + value; 885 + if (!npadch_active) { 886 + npadch_value = 0; 887 + npadch_active = true; 888 + } 889 + 890 + npadch_value = npadch_value * base + value; 893 891 } 894 892 895 893 static void k_lock(struct vc_data *vc, unsigned char value, char up_flag)