diff mbox series

ati-vga: increment mm_index in ati_mm_read/write

Message ID 20200603124732.1137892-1-ppandit@redhat.com
State New
Headers show
Series ati-vga: increment mm_index in ati_mm_read/write | expand

Commit Message

Prasad Pandit June 3, 2020, 12:47 p.m. UTC
From: Prasad J Pandit <pjp@fedoraproject.org>

While accessing VGA registers via ati_mm_read/write routines,
a guest may set 's->regs.mm_index' such that it leads to infinite
recursion. Increment the mm_index value to avoid it.

Reported-by: Ren Ding <rding@gatech.edu>
Reported-by: Hanqing Zhao <hanqing@gatech.edu>
Reported-by: Yi Ren <c4tren@gmail.com>
Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
---
 hw/display/ati.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

Comments

Gerd Hoffmann June 3, 2020, 1:44 p.m. UTC | #1
On Wed, Jun 03, 2020 at 06:17:32PM +0530, P J P wrote:
> From: Prasad J Pandit <pjp@fedoraproject.org>
> 
> While accessing VGA registers via ati_mm_read/write routines,
> a guest may set 's->regs.mm_index' such that it leads to infinite
> recursion.

Lovely.

> Increment the mm_index value to avoid it.

Hmm, why modify mm_index?  Shouldn't we just check it is non-zero
before calling ati_mm_read/ati_mm_write?

cheers,
  Gerd
BALATON Zoltan June 3, 2020, 1:56 p.m. UTC | #2
On Wed, 3 Jun 2020, Gerd Hoffmann wrote:
> On Wed, Jun 03, 2020 at 06:17:32PM +0530, P J P wrote:
>> From: Prasad J Pandit <pjp@fedoraproject.org>
>>
>> While accessing VGA registers via ati_mm_read/write routines,
>> a guest may set 's->regs.mm_index' such that it leads to infinite
>> recursion.
>
> Lovely.
>
>> Increment the mm_index value to avoid it.
>
> Hmm, why modify mm_index?  Shouldn't we just check it is non-zero
> before calling ati_mm_read/ati_mm_write?

I haven't found any mention in any docs that say MM_INDEX should auto 
increment so unless this is proven to do that on real hardware I also 
think forbiding indexed access to index registers should be enough.

Regards,
BALATON Zoltan
Prasad Pandit June 3, 2020, 2:35 p.m. UTC | #3
+-- On Wed, 3 Jun 2020, Gerd Hoffmann wrote --+
| Hmm, why modify mm_index?  Shouldn't we just check it is non-zero
| before calling ati_mm_read/ati_mm_write?

  if (s->regs.mm_index & BIT(31)) {
     ...
  } else {
     ati_mm_write(s, s->regs.mm_index + addr - MM_DATA, data, size);
  }

Exit condition for recursion is to set (mm_index & BIT(31)), so recursion 
would continue even with non-zero values I think.

Thank you.
--
Prasad J Pandit / Red Hat Product Security Team
8685 545E B54C 486B C6EB 271E E285 8B5A F050 DE8D
Gerd Hoffmann June 3, 2020, 3:32 p.m. UTC | #4
On Wed, Jun 03, 2020 at 08:05:50PM +0530, P J P wrote:
> +-- On Wed, 3 Jun 2020, Gerd Hoffmann wrote --+
> | Hmm, why modify mm_index?  Shouldn't we just check it is non-zero
> | before calling ati_mm_read/ati_mm_write?
> 
>   if (s->regs.mm_index & BIT(31)) {
>      ...
>   } else {

} else if (s->regs.mm_index > 3) {

>      ati_mm_write(s, s->regs.mm_index + addr - MM_DATA, data, size);
>   }
> 
> Exit condition for recursion is to set (mm_index & BIT(31)), so recursion 
> would continue even with non-zero values I think.

It's wrapped into a case switch for all the registers.  The code quoted
above is only entered for "addr >= MM_DATA && addr <= MM_DATA+3", so the
check should stop recursion.  A less strict "s->regs.mm_index > 0" may
recurse a few times but will not recurse endless either.

cheers,
  Gerd
BALATON Zoltan June 3, 2020, 3:38 p.m. UTC | #5
On Wed, 3 Jun 2020, P J P wrote:
> +-- On Wed, 3 Jun 2020, Gerd Hoffmann wrote --+
> | Hmm, why modify mm_index?  Shouldn't we just check it is non-zero
> | before calling ati_mm_read/ati_mm_write?
>
>  if (s->regs.mm_index & BIT(31)) {
>     ...
>  } else {
>     ati_mm_write(s, s->regs.mm_index + addr - MM_DATA, data, size);
>  }
>
> Exit condition for recursion is to set (mm_index & BIT(31)), so recursion
> would continue even with non-zero values I think.

MM_INDEX and MM_DATA allows access to registers (or memory if BIT(31) is 
set) via only these two registers. This is called indexed access in docs. 
So it does not really make sense to allow access to these registers as 
well thus we can avoid infinite recursion. It's not meant to recurse until 
BIT(31) is set. I think you can do:

   if (s->regs.mm_index & BIT(31)) {
      ...
-  } else {
+  } else if (s->regs.mm_index >= BIOS_0_SCRATCH) {
      ati_mm_write(s, s->regs.mm_index + addr - MM_DATA, data, size);
   }

and do the same in ati_mm_read() as well.

Regards,
BALATON Zoltan
BALATON Zoltan June 3, 2020, 3:43 p.m. UTC | #6
On Wed, 3 Jun 2020, BALATON Zoltan wrote:
> On Wed, 3 Jun 2020, P J P wrote:
>> +-- On Wed, 3 Jun 2020, Gerd Hoffmann wrote --+
>> | Hmm, why modify mm_index?  Shouldn't we just check it is non-zero
>> | before calling ati_mm_read/ati_mm_write?
>>
>>  if (s->regs.mm_index & BIT(31)) {
>>     ...
>>  } else {
>>     ati_mm_write(s, s->regs.mm_index + addr - MM_DATA, data, size);
>>  }
>> 
>> Exit condition for recursion is to set (mm_index & BIT(31)), so recursion
>> would continue even with non-zero values I think.
>
> MM_INDEX and MM_DATA allows access to registers (or memory if BIT(31) is set) 
> via only these two registers. This is called indexed access in docs. So it 
> does not really make sense to allow access to these registers as well thus we 
> can avoid infinite recursion. It's not meant to recurse until BIT(31) is set. 
> I think you can do:
>
>  if (s->regs.mm_index & BIT(31)) {
>     ...
> -  } else {
> +  } else if (s->regs.mm_index >= BIOS_0_SCRATCH) {

Actually this should be

+  } else if (s->regs.mm_index >= CLOCK_CNTL_INDEX) {

or even > MM_DATA + 3 may be best as that only refers to defines used in 
that case. So maybe

+  } else if (s->regs.mm_index > MM_DATA + 3) {

>     ati_mm_write(s, s->regs.mm_index + addr - MM_DATA, data, size);
>  }
>
> and do the same in ati_mm_read() as well.
>
> Regards,
> BALATON Zoltan
>
Prasad Pandit June 3, 2020, 7:08 p.m. UTC | #7
+-- On Wed, 3 Jun 2020, BALATON Zoltan wrote --+
| or even > MM_DATA + 3 may be best as that only refers to defines used in 
| that case. So maybe
| 
| +  } else if (s->regs.mm_index > MM_DATA + 3) {
| >     ati_mm_write(s, s->regs.mm_index + addr - MM_DATA, data, size);
| >  }
| >
| > and do the same in ati_mm_read() as well.

Sent patch v2. Thank you.
--
Prasad J Pandit / Red Hat Product Security Team
8685 545E B54C 486B C6EB 271E E285 8B5A F050 DE8D
diff mbox series

Patch

diff --git a/hw/display/ati.c b/hw/display/ati.c
index 065f197678..fa9061ad0b 100644
--- a/hw/display/ati.c
+++ b/hw/display/ati.c
@@ -286,7 +286,8 @@  static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size)
                 val = ldn_le_p(s->vga.vram_ptr + idx, size);
             }
         } else {
-            val = ati_mm_read(s, s->regs.mm_index + addr - MM_DATA, size);
+            uint32_t idx = s->regs.mm_index++;
+            val = ati_mm_read(s, idx + addr - MM_DATA, size);
         }
         break;
     case BIOS_0_SCRATCH ... BUS_CNTL - 1:
@@ -521,7 +522,8 @@  static void ati_mm_write(void *opaque, hwaddr addr,
                 stn_le_p(s->vga.vram_ptr + idx, size, data);
             }
         } else {
-            ati_mm_write(s, s->regs.mm_index + addr - MM_DATA, data, size);
+            uint32_t idx = s->regs.mm_index++;
+            ati_mm_write(s, idx + addr - MM_DATA, data, size);
         }
         break;
     case BIOS_0_SCRATCH ... BUS_CNTL - 1: