diff mbox

[m25p80] Abort in case we overrun the internal data buffer

Message ID 20170103211705.27876-1-jcd@tribudubois.net
State New
Headers show

Commit Message

Jean-Christophe DUBOIS Jan. 3, 2017, 9:17 p.m. UTC
Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
---
 hw/block/m25p80.c | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

Comments

Peter Maydell Jan. 5, 2017, 6:38 p.m. UTC | #1
On 3 January 2017 at 21:17, Jean-Christophe Dubois <jcd@tribudubois.net> wrote:
> Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
> ---
>  hw/block/m25p80.c | 19 +++++++++++++++++--
>  1 file changed, 17 insertions(+), 2 deletions(-)
>
> diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
> index d29ff4c..6c374cf 100644
> --- a/hw/block/m25p80.c
> +++ b/hw/block/m25p80.c
> @@ -28,6 +28,7 @@
>  #include "hw/ssi/ssi.h"
>  #include "qemu/bitops.h"
>  #include "qemu/log.h"
> +#include "qemu/error-report.h"
>  #include "qapi/error.h"
>
>  #ifndef M25P80_ERR_DEBUG
> @@ -376,6 +377,8 @@ typedef enum {
>      MAN_GENERIC,
>  } Manufacturer;
>
> +#define _INTERNAL_DATA_SIZE 16
> +

Don't use leading underscores, please.

>  typedef struct Flash {
>      SSISlave parent_obj;
>
> @@ -386,7 +389,7 @@ typedef struct Flash {
>      int page_size;
>
>      uint8_t state;
> -    uint8_t data[16];
> +    uint8_t data[_INTERNAL_DATA_SIZE];
>      uint32_t len;
>      uint32_t pos;
>      uint8_t needed_bytes;
> @@ -1114,6 +1117,12 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx)
>
>      case STATE_COLLECTING_DATA:
>      case STATE_COLLECTING_VAR_LEN_DATA:
> +
> +        if (s->len >= _INTERNAL_DATA_SIZE) {
> +            error_report("Bug - Write overrun internal data buffer");
> +            abort();
> +        }
> +
>          s->data[s->len] = (uint8_t)tx;
>          s->len++;
>
> @@ -1123,6 +1132,12 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx)
>          break;
>
>      case STATE_READING_DATA:
> +
> +        if (s->pos >= _INTERNAL_DATA_SIZE) {
> +            error_report("Bug - Read overrun internal data buffer");
> +            abort();
> +        }
> +

If these are "can't happen unless some other part of QEMU
is buggy" cases, then we can just assert():

    assert(s->pos < ARRAY_SIZE(s->data));

A comment about what kind of other part of QEMU might be buggy
if the assertion fires would also be helpful for future readers.

(If they're "could happen if the guest does something wrong"
cases, we shouldn't just abort(), but if I'm reading the previous
mail thread correctly, that's not the situation here.)

>          r = s->data[s->pos];
>          s->pos++;
>          if (s->pos == s->len) {
> @@ -1195,7 +1210,7 @@ static const VMStateDescription vmstate_m25p80 = {
>      .pre_save = m25p80_pre_save,
>      .fields = (VMStateField[]) {
>          VMSTATE_UINT8(state, Flash),
> -        VMSTATE_UINT8_ARRAY(data, Flash, 16),
> +        VMSTATE_UINT8_ARRAY(data, Flash, _INTERNAL_DATA_SIZE),
>          VMSTATE_UINT32(len, Flash),
>          VMSTATE_UINT32(pos, Flash),
>          VMSTATE_UINT8(needed_bytes, Flash),
> --
> 2.9.3

thanks
-- PMM
mar.krzeminski Jan. 5, 2017, 8:04 p.m. UTC | #2
Hi Peter,

W dniu 05.01.2017 o 19:38, Peter Maydell pisze:
> On 3 January 2017 at 21:17, Jean-Christophe Dubois <jcd@tribudubois.net> wrote:
>> Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
>> ---
>>   hw/block/m25p80.c | 19 +++++++++++++++++--
>>   1 file changed, 17 insertions(+), 2 deletions(-)
>>
>> diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
>> index d29ff4c..6c374cf 100644
>> --- a/hw/block/m25p80.c
>> +++ b/hw/block/m25p80.c
>> @@ -28,6 +28,7 @@
>>   #include "hw/ssi/ssi.h"
>>   #include "qemu/bitops.h"
>>   #include "qemu/log.h"
>> +#include "qemu/error-report.h"
>>   #include "qapi/error.h"
>>
>>   #ifndef M25P80_ERR_DEBUG
>> @@ -376,6 +377,8 @@ typedef enum {
>>       MAN_GENERIC,
>>   } Manufacturer;
>>
>> +#define _INTERNAL_DATA_SIZE 16
>> +
> Don't use leading underscores, please.
>
>>   typedef struct Flash {
>>       SSISlave parent_obj;
>>
>> @@ -386,7 +389,7 @@ typedef struct Flash {
>>       int page_size;
>>
>>       uint8_t state;
>> -    uint8_t data[16];
>> +    uint8_t data[_INTERNAL_DATA_SIZE];
>>       uint32_t len;
>>       uint32_t pos;
>>       uint8_t needed_bytes;
>> @@ -1114,6 +1117,12 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx)
>>
>>       case STATE_COLLECTING_DATA:
>>       case STATE_COLLECTING_VAR_LEN_DATA:
>> +
>> +        if (s->len >= _INTERNAL_DATA_SIZE) {
>> +            error_report("Bug - Write overrun internal data buffer");
>> +            abort();
>> +        }
>> +
>>           s->data[s->len] = (uint8_t)tx;
>>           s->len++;
>>
>> @@ -1123,6 +1132,12 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx)
>>           break;
>>
>>       case STATE_READING_DATA:
>> +
>> +        if (s->pos >= _INTERNAL_DATA_SIZE) {
>> +            error_report("Bug - Read overrun internal data buffer");
>> +            abort();
>> +        }
>> +
> If these are "can't happen unless some other part of QEMU
> is buggy" cases, then we can just assert():
>
>      assert(s->pos < ARRAY_SIZE(s->data));
>
> A comment about what kind of other part of QEMU might be buggy
> if the assertion fires would also be helpful for future readers.
>
> (If they're "could happen if the guest does something wrong"
> cases, we shouldn't just abort(), but if I'm reading the previous
> mail thread correctly, that's not the situation here.)
Indeed this case is about error in Qemu itself, but the same situation could
be generated from the guest (guest deasert CS only once).
IMHO we should reset m26p80 state in such case:
s->len = 0;
s->pos = 0;
s->state = STATE_IDLE;
This will be a bit closer to real HW behaviour too.

Thanks,
Marcin

>
>>           r = s->data[s->pos];
>>           s->pos++;
>>           if (s->pos == s->len) {
>> @@ -1195,7 +1210,7 @@ static const VMStateDescription vmstate_m25p80 = {
>>       .pre_save = m25p80_pre_save,
>>       .fields = (VMStateField[]) {
>>           VMSTATE_UINT8(state, Flash),
>> -        VMSTATE_UINT8_ARRAY(data, Flash, 16),
>> +        VMSTATE_UINT8_ARRAY(data, Flash, _INTERNAL_DATA_SIZE),
>>           VMSTATE_UINT32(len, Flash),
>>           VMSTATE_UINT32(pos, Flash),
>>           VMSTATE_UINT8(needed_bytes, Flash),
>> --
>> 2.9.3
> thanks
> -- PMM
>
Jean-Christophe DUBOIS Jan. 5, 2017, 8:18 p.m. UTC | #3
Le 05/01/2017 à 21:04, mar.krzeminski a écrit :
> Hi Peter,
>
> W dniu 05.01.2017 o 19:38, Peter Maydell pisze:
>> On 3 January 2017 at 21:17, Jean-Christophe Dubois 
>> <jcd@tribudubois.net> wrote:
>>> Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
>>> ---
>>>   hw/block/m25p80.c | 19 +++++++++++++++++--
>>>   1 file changed, 17 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
>>> index d29ff4c..6c374cf 100644
>>> --- a/hw/block/m25p80.c
>>> +++ b/hw/block/m25p80.c
>>> @@ -28,6 +28,7 @@
>>>   #include "hw/ssi/ssi.h"
>>>   #include "qemu/bitops.h"
>>>   #include "qemu/log.h"
>>> +#include "qemu/error-report.h"
>>>   #include "qapi/error.h"
>>>
>>>   #ifndef M25P80_ERR_DEBUG
>>> @@ -376,6 +377,8 @@ typedef enum {
>>>       MAN_GENERIC,
>>>   } Manufacturer;
>>>
>>> +#define _INTERNAL_DATA_SIZE 16
>>> +
>> Don't use leading underscores, please.
>>
>>>   typedef struct Flash {
>>>       SSISlave parent_obj;
>>>
>>> @@ -386,7 +389,7 @@ typedef struct Flash {
>>>       int page_size;
>>>
>>>       uint8_t state;
>>> -    uint8_t data[16];
>>> +    uint8_t data[_INTERNAL_DATA_SIZE];
>>>       uint32_t len;
>>>       uint32_t pos;
>>>       uint8_t needed_bytes;
>>> @@ -1114,6 +1117,12 @@ static uint32_t m25p80_transfer8(SSISlave 
>>> *ss, uint32_t tx)
>>>
>>>       case STATE_COLLECTING_DATA:
>>>       case STATE_COLLECTING_VAR_LEN_DATA:
>>> +
>>> +        if (s->len >= _INTERNAL_DATA_SIZE) {
>>> +            error_report("Bug - Write overrun internal data buffer");
>>> +            abort();
>>> +        }
>>> +
>>>           s->data[s->len] = (uint8_t)tx;
>>>           s->len++;
>>>
>>> @@ -1123,6 +1132,12 @@ static uint32_t m25p80_transfer8(SSISlave 
>>> *ss, uint32_t tx)
>>>           break;
>>>
>>>       case STATE_READING_DATA:
>>> +
>>> +        if (s->pos >= _INTERNAL_DATA_SIZE) {
>>> +            error_report("Bug - Read overrun internal data buffer");
>>> +            abort();
>>> +        }
>>> +
>> If these are "can't happen unless some other part of QEMU
>> is buggy" cases, then we can just assert():
>>
>>      assert(s->pos < ARRAY_SIZE(s->data));
>>
>> A comment about what kind of other part of QEMU might be buggy
>> if the assertion fires would also be helpful for future readers.
>>
>> (If they're "could happen if the guest does something wrong"
>> cases, we shouldn't just abort(), but if I'm reading the previous
>> mail thread correctly, that's not the situation here.)
> Indeed this case is about error in Qemu itself, but the same situation 
> could
> be generated from the guest (guest deasert CS only once).
> IMHO we should reset m26p80 state in such case:
> s->len = 0;
> s->pos = 0;
> s->state = STATE_IDLE;
> This will be a bit closer to real HW behaviour too.

So what would be the preferred behavior?

  * Asserting (and ending Qemu)
  * Resetting (and hiding the misbehavior).

JC


>
> Thanks,
> Marcin
>
>>
>>>           r = s->data[s->pos];
>>>           s->pos++;
>>>           if (s->pos == s->len) {
>>> @@ -1195,7 +1210,7 @@ static const VMStateDescription vmstate_m25p80 
>>> = {
>>>       .pre_save = m25p80_pre_save,
>>>       .fields = (VMStateField[]) {
>>>           VMSTATE_UINT8(state, Flash),
>>> -        VMSTATE_UINT8_ARRAY(data, Flash, 16),
>>> +        VMSTATE_UINT8_ARRAY(data, Flash, _INTERNAL_DATA_SIZE),
>>>           VMSTATE_UINT32(len, Flash),
>>>           VMSTATE_UINT32(pos, Flash),
>>>           VMSTATE_UINT8(needed_bytes, Flash),
>>> -- 
>>> 2.9.3
>> thanks
>> -- PMM
>>
>
>
Peter Maydell Jan. 5, 2017, 8:51 p.m. UTC | #4
On 5 January 2017 at 20:18, Jean-Christophe DUBOIS <jcd@tribudubois.net> wrote:
> Le 05/01/2017 à 21:04, mar.krzeminski a écrit :
>> Peter Maydell wrote:
>>> If these are "can't happen unless some other part of QEMU
>>> is buggy" cases, then we can just assert():

>>> (If they're "could happen if the guest does something wrong"
>>> cases, we shouldn't just abort(), but if I'm reading the previous
>>> mail thread correctly, that's not the situation here.)
>
>> Indeed this case is about error in Qemu itself, but the same situation could
>> be generated from the guest (guest deasert CS only once).
>> IMHO we should reset m26p80 state in such case:
>> s->len = 0;
>> s->pos = 0;
>> s->state = STATE_IDLE;
>> This will be a bit closer to real HW behaviour too.

> So what would be the preferred behavior?
>
> Asserting (and ending Qemu)
> Resetting (and hiding the misbehavior).

If the guest can trigger this behaviour, then we should
not assert or abort or otherwise cause QEMU to exit.
The preferred behaviour is:
 * act like the real hardware does in this situation
   (whatever that is)
 * if this is something that only broken guest code would
   do, log it with qemu_log_mask(LOG_GUEST_ERROR, ...)

thanks
-- PMM
Jean-Christophe DUBOIS Jan. 5, 2017, 9:39 p.m. UTC | #5
Le 05/01/2017 à 21:51, Peter Maydell a écrit :
>> So what would be the preferred behavior?
>>
>> Asserting (and ending Qemu)
>> Resetting (and hiding the misbehavior).
> If the guest can trigger this behaviour, then we should
> not assert or abort or otherwise cause QEMU to exit.
> The preferred behaviour is:
>   * act like the real hardware does in this situation
>     (whatever that is)
>   * if this is something that only broken guest code would
>     do, log it with qemu_log_mask(LOG_GUEST_ERROR, ...)

I guess a real SPI controllers should not trigger this behavior like I 
did in the i.MX SPI controller emulation. It was a bug in my code and 
the crash (SIGSEGV) forced me to try to find a solution. At first I 
tried to fix the SPI device instead of the SPI controller because a 
SIGSEGV did not seem like an expected reaction and I was missing hints 
that my code was misbehaving.

But it is also possible for a guest to emulates the SPI controller 
through bit banging (for example). And in this case the guest could end 
up misbehaving in its software implementation.

So I think this behavior could be triggered either by buggy SPI 
controller emulator or by buggy guest software. And it seems hard to 
determine where the fault comes from from within Qemu. Obviously if the 
fault is in a Qemu emulator we would not want to tag the error as a 
LOG_GUEST_ERROR. On the other hand, it would be nice to warn the user if 
the guest if it is indeed misbehaving so that he can fix his code.

>
> thanks
> -- PMM
>
>
Peter Maydell Jan. 6, 2017, 10:18 a.m. UTC | #6
On 5 January 2017 at 21:39, Jean-Christophe DUBOIS <jcd@tribudubois.net> wrote:
> So I think this behavior could be triggered either by buggy SPI controller
> emulator or by buggy guest software. And it seems hard to determine where
> the fault comes from from within Qemu. Obviously if the fault is in a Qemu
> emulator we would not want to tag the error as a LOG_GUEST_ERROR. On the
> other hand, it would be nice to warn the user if the guest if it is indeed
> misbehaving so that he can fix his code.

We should log it as a guest error. "warning might be wrong if there's
a bug in QEMU" is true of pretty much any warning we emit...

thanks
-- PMM
Jean-Christophe DUBOIS Jan. 6, 2017, 6:20 p.m. UTC | #7
Le 06/01/2017 à 11:18, Peter Maydell a écrit :
> On 5 January 2017 at 21:39, Jean-Christophe DUBOIS <jcd@tribudubois.net> wrote:
>> So I think this behavior could be triggered either by buggy SPI controller
>> emulator or by buggy guest software. And it seems hard to determine where
>> the fault comes from from within Qemu. Obviously if the fault is in a Qemu
>> emulator we would not want to tag the error as a LOG_GUEST_ERROR. On the
>> other hand, it would be nice to warn the user if the guest if it is indeed
>> misbehaving so that he can fix his code.
> We should log it as a guest error. "warning might be wrong if there's
> a bug in QEMU" is true of pretty much any warning we emit...

OK, I'll do this.

JC

>
> thanks
> -- PMM
>
diff mbox

Patch

diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index d29ff4c..6c374cf 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -28,6 +28,7 @@ 
 #include "hw/ssi/ssi.h"
 #include "qemu/bitops.h"
 #include "qemu/log.h"
+#include "qemu/error-report.h"
 #include "qapi/error.h"
 
 #ifndef M25P80_ERR_DEBUG
@@ -376,6 +377,8 @@  typedef enum {
     MAN_GENERIC,
 } Manufacturer;
 
+#define _INTERNAL_DATA_SIZE 16
+
 typedef struct Flash {
     SSISlave parent_obj;
 
@@ -386,7 +389,7 @@  typedef struct Flash {
     int page_size;
 
     uint8_t state;
-    uint8_t data[16];
+    uint8_t data[_INTERNAL_DATA_SIZE];
     uint32_t len;
     uint32_t pos;
     uint8_t needed_bytes;
@@ -1114,6 +1117,12 @@  static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx)
 
     case STATE_COLLECTING_DATA:
     case STATE_COLLECTING_VAR_LEN_DATA:
+
+        if (s->len >= _INTERNAL_DATA_SIZE) {
+            error_report("Bug - Write overrun internal data buffer");
+            abort();
+        }
+
         s->data[s->len] = (uint8_t)tx;
         s->len++;
 
@@ -1123,6 +1132,12 @@  static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx)
         break;
 
     case STATE_READING_DATA:
+
+        if (s->pos >= _INTERNAL_DATA_SIZE) {
+            error_report("Bug - Read overrun internal data buffer");
+            abort();
+        }
+
         r = s->data[s->pos];
         s->pos++;
         if (s->pos == s->len) {
@@ -1195,7 +1210,7 @@  static const VMStateDescription vmstate_m25p80 = {
     .pre_save = m25p80_pre_save,
     .fields = (VMStateField[]) {
         VMSTATE_UINT8(state, Flash),
-        VMSTATE_UINT8_ARRAY(data, Flash, 16),
+        VMSTATE_UINT8_ARRAY(data, Flash, _INTERNAL_DATA_SIZE),
         VMSTATE_UINT32(len, Flash),
         VMSTATE_UINT32(pos, Flash),
         VMSTATE_UINT8(needed_bytes, Flash),