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

powerpc/pseries/hvconsole: Fix stack overread via udbg

While developing KASAN for 64-bit book3s, I hit the following stack
over-read.

It occurs because the hypercall to put characters onto the terminal
takes 2 longs (128 bits/16 bytes) of characters at a time, and so
hvc_put_chars() would unconditionally copy 16 bytes from the argument
buffer, regardless of supplied length. However, udbg_hvc_putc() can
call hvc_put_chars() with a single-byte buffer, leading to the error.

==================================================================
BUG: KASAN: stack-out-of-bounds in hvc_put_chars+0xdc/0x110
Read of size 8 at addr c0000000023e7a90 by task swapper/0

CPU: 0 PID: 0 Comm: swapper Not tainted 5.2.0-rc2-next-20190528-02824-g048a6ab4835b #113
Call Trace:
dump_stack+0x104/0x154 (unreliable)
print_address_description+0xa0/0x30c
__kasan_report+0x20c/0x224
kasan_report+0x18/0x30
__asan_report_load8_noabort+0x24/0x40
hvc_put_chars+0xdc/0x110
hvterm_raw_put_chars+0x9c/0x110
udbg_hvc_putc+0x154/0x200
udbg_write+0xf0/0x240
console_unlock+0x868/0xd30
register_console+0x970/0xe90
register_early_udbg_console+0xf8/0x114
setup_arch+0x108/0x790
start_kernel+0x104/0x784
start_here_common+0x1c/0x534

Memory state around the buggy address:
c0000000023e7980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
c0000000023e7a00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1
>c0000000023e7a80: f1 f1 01 f2 f2 f2 00 00 00 00 00 00 00 00 00 00
^
c0000000023e7b00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
c0000000023e7b80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
==================================================================

Document that a 16-byte buffer is requred, and provide it in udbg.

Signed-off-by: Daniel Axtens <dja@axtens.net>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>

authored by

Daniel Axtens and committed by
Michael Ellerman
934bda59 2305ff22

+16 -2
+1 -1
arch/powerpc/platforms/pseries/hvconsole.c
··· 62 62 * @vtermno: The vtermno or unit_address of the adapter from which the data 63 63 * originated. 64 64 * @buf: The character buffer that contains the character data to send to 65 - * firmware. 65 + * firmware. Must be at least 16 bytes, even if count is less than 16. 66 66 * @count: Send this number of characters. 67 67 */ 68 68 int hvc_put_chars(uint32_t vtermno, const char *buf, int count)
+15 -1
drivers/tty/hvc/hvc_vio.c
··· 107 107 return got; 108 108 } 109 109 110 + /** 111 + * hvterm_raw_put_chars: send characters to firmware for given vterm adapter 112 + * @vtermno: The virtual terminal number. 113 + * @buf: The characters to send. Because of the underlying hypercall in 114 + * hvc_put_chars(), this buffer must be at least 16 bytes long, even if 115 + * you are sending fewer chars. 116 + * @count: number of chars to send. 117 + */ 110 118 static int hvterm_raw_put_chars(uint32_t vtermno, const char *buf, int count) 111 119 { 112 120 struct hvterm_priv *pv = hvterm_privs[vtermno]; ··· 227 219 static void udbg_hvc_putc(char c) 228 220 { 229 221 int count = -1; 222 + unsigned char bounce_buffer[16]; 230 223 231 224 if (!hvterm_privs[0]) 232 225 return; ··· 238 229 do { 239 230 switch(hvterm_privs[0]->proto) { 240 231 case HV_PROTOCOL_RAW: 241 - count = hvterm_raw_put_chars(0, &c, 1); 232 + /* 233 + * hvterm_raw_put_chars requires at least a 16-byte 234 + * buffer, so go via the bounce buffer 235 + */ 236 + bounce_buffer[0] = c; 237 + count = hvterm_raw_put_chars(0, bounce_buffer, 1); 242 238 break; 243 239 case HV_PROTOCOL_HVSI: 244 240 count = hvterm_hvsi_put_chars(0, &c, 1);