Commit 934bda59 authored by Daniel Axtens's avatar Daniel Axtens Committed by Michael Ellerman

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: default avatarDaniel Axtens <dja@axtens.net>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent 2305ff22
...@@ -62,7 +62,7 @@ EXPORT_SYMBOL(hvc_get_chars); ...@@ -62,7 +62,7 @@ EXPORT_SYMBOL(hvc_get_chars);
* @vtermno: The vtermno or unit_address of the adapter from which the data * @vtermno: The vtermno or unit_address of the adapter from which the data
* originated. * originated.
* @buf: The character buffer that contains the character data to send to * @buf: The character buffer that contains the character data to send to
* firmware. * firmware. Must be at least 16 bytes, even if count is less than 16.
* @count: Send this number of characters. * @count: Send this number of characters.
*/ */
int hvc_put_chars(uint32_t vtermno, const char *buf, int count) int hvc_put_chars(uint32_t vtermno, const char *buf, int count)
......
...@@ -107,6 +107,14 @@ static int hvterm_raw_get_chars(uint32_t vtermno, char *buf, int count) ...@@ -107,6 +107,14 @@ static int hvterm_raw_get_chars(uint32_t vtermno, char *buf, int count)
return got; return got;
} }
/**
* hvterm_raw_put_chars: send characters to firmware for given vterm adapter
* @vtermno: The virtual terminal number.
* @buf: The characters to send. Because of the underlying hypercall in
* hvc_put_chars(), this buffer must be at least 16 bytes long, even if
* you are sending fewer chars.
* @count: number of chars to send.
*/
static int hvterm_raw_put_chars(uint32_t vtermno, const char *buf, int count) static int hvterm_raw_put_chars(uint32_t vtermno, const char *buf, int count)
{ {
struct hvterm_priv *pv = hvterm_privs[vtermno]; struct hvterm_priv *pv = hvterm_privs[vtermno];
...@@ -219,6 +227,7 @@ static const struct hv_ops hvterm_hvsi_ops = { ...@@ -219,6 +227,7 @@ static const struct hv_ops hvterm_hvsi_ops = {
static void udbg_hvc_putc(char c) static void udbg_hvc_putc(char c)
{ {
int count = -1; int count = -1;
unsigned char bounce_buffer[16];
if (!hvterm_privs[0]) if (!hvterm_privs[0])
return; return;
...@@ -229,7 +238,12 @@ static void udbg_hvc_putc(char c) ...@@ -229,7 +238,12 @@ static void udbg_hvc_putc(char c)
do { do {
switch(hvterm_privs[0]->proto) { switch(hvterm_privs[0]->proto) {
case HV_PROTOCOL_RAW: case HV_PROTOCOL_RAW:
count = hvterm_raw_put_chars(0, &c, 1); /*
* hvterm_raw_put_chars requires at least a 16-byte
* buffer, so go via the bounce buffer
*/
bounce_buffer[0] = c;
count = hvterm_raw_put_chars(0, bounce_buffer, 1);
break; break;
case HV_PROTOCOL_HVSI: case HV_PROTOCOL_HVSI:
count = hvterm_hvsi_put_chars(0, &c, 1); count = hvterm_hvsi_put_chars(0, &c, 1);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment