diff mbox series

ccid-card-passthru: check buffer size parameter

Message ID 20181011112436.9305-1-ppandit@redhat.com (mailing list archive)
State New, archived
Headers show
Series ccid-card-passthru: check buffer size parameter | expand

Commit Message

Prasad Pandit Oct. 11, 2018, 11:24 a.m. UTC
From: Prasad J Pandit <pjp@fedoraproject.org>

While reading virtual smart card data, if buffer 'size' is negative
it would lead to memory corruption errors. Add check to avoid it.

Reported-by: Arash TC <tohidi.arash@gmail.com>
Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
---
 hw/usb/ccid-card-passthru.c | 1 +
 1 file changed, 1 insertion(+)

Comments

Philippe Mathieu-Daudé Oct. 11, 2018, 11:58 a.m. UTC | #1
Cc'ing Paolo & Marc-André.

On 11/10/2018 13:24, P J P wrote:
> From: Prasad J Pandit <pjp@fedoraproject.org>
> 
> While reading virtual smart card data, if buffer 'size' is negative
> it would lead to memory corruption errors. Add check to avoid it.

The IOReadHandler does not have documentation.

 typedef void IOReadHandler(void *opaque, const uint8_t *buf, int size);

Why is the 'size' argument signed? Does it makes sens to call it with a
negative value?

Thanks,

Phil.

> 
> Reported-by: Arash TC <tohidi.arash@gmail.com>
> Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
> ---
>  hw/usb/ccid-card-passthru.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c
> index 0a6c657228..63ed78f4c6 100644
> --- a/hw/usb/ccid-card-passthru.c
> +++ b/hw/usb/ccid-card-passthru.c
> @@ -275,6 +275,7 @@ static void ccid_card_vscard_read(void *opaque, const uint8_t *buf, int size)
>      PassthruState *card = opaque;
>      VSCMsgHeader *hdr;
>  
> +    assert(0 <= size && size < VSCARD_IN_SIZE);
>      if (card->vscard_in_pos + size > VSCARD_IN_SIZE) {
>          error_report("no room for data: pos %u +  size %d > %" PRId64 "."
>                       " dropping connection.",
>
Paolo Bonzini Oct. 11, 2018, 12:15 p.m. UTC | #2
On 11/10/2018 13:58, Philippe Mathieu-Daudé wrote:
> Cc'ing Paolo & Marc-André.
> 
> On 11/10/2018 13:24, P J P wrote:
>> From: Prasad J Pandit <pjp@fedoraproject.org>
>>
>> While reading virtual smart card data, if buffer 'size' is negative
>> it would lead to memory corruption errors. Add check to avoid it.
> 
> The IOReadHandler does not have documentation.
> 
>  typedef void IOReadHandler(void *opaque, const uint8_t *buf, int size);
> 
> Why is the 'size' argument signed? Does it makes sens to call it with a
> negative value?

Yeah, it probably should be changed to size_t (same for the return value
of can_read callbacks); the size cannot be negative, in fact it can also
never be zero.

Also, the "if" should never trigger because the size is bound to what
can_read returns, which is VSCARD_IN_SIZE - card->vscard_in_pos.

So it is probably better to:

1) move the

    assert(card->vscard_in_pos < VSCARD_IN_SIZE);

to can_read, which becomes

    assert(card->vscard_in_pos <= VSCARD_IN_SIZE);
    return VSCARD_IN_SIZE - card->vscard_in_pos;

2) here, replace the if with an

    assert(size <= VSCARD_IN_SIZE - card->vscard_in_pos);

Note that the right side of the comparison is the return value of
can_read.  Also, this assert will always fail if card->vscard_in_pos >=
VSCARD_IN_SIZE), since size > 0.

3) also here, replace the

    assert(card->vscard_in_hdr < VSCARD_IN_SIZE);

with the stricter and more correct

    assert(card->vscard_in_hdr < card->vscard_in_pos);


Related, I don't understand why the read function ends with

    if (card->vscard_in_hdr == card->vscard_in_pos) {
        card->vscard_in_pos = card->vscard_in_hdr = 0;
    }

I would have expected something more generic, like:

    /* Drop any messages that were fully processed.  */
    if (card->vscard_in_hdr > 0) {
        memmove(card->vscard_in_data,
                card->vscard_in_data + card->vscard_in_hdr,
                card->vscard_in_pos - card->vscard_in_hdr);
        card->vscard_in_pos -= card->vscard_in_hdr;
        card->vscard_in_hdr = 0;
    }

Marc-André, do you know something about libcacard that justifies the
latter?  If the error_report is changed to an assert it's probably a
good idea anyway to make the code more liberal in what it accepts.
Prasad, can you prepare a v2 that does these changes?

Thanks,

Paolo

> 
> Thanks,
> 
> Phil.
> 
>>
>> Reported-by: Arash TC <tohidi.arash@gmail.com>
>> Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
>> ---
>>  hw/usb/ccid-card-passthru.c | 1 +
>>  1 file changed, 1 insertion(+)
>>
>> diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c
>> index 0a6c657228..63ed78f4c6 100644
>> --- a/hw/usb/ccid-card-passthru.c
>> +++ b/hw/usb/ccid-card-passthru.c
>> @@ -275,6 +275,7 @@ static void ccid_card_vscard_read(void *opaque, const uint8_t *buf, int size)
>>      PassthruState *card = opaque;
>>      VSCMsgHeader *hdr;
>>  
>> +    assert(0 <= size && size < VSCARD_IN_SIZE);
>>      if (card->vscard_in_pos + size > VSCARD_IN_SIZE) {
>>          error_report("no room for data: pos %u +  size %d > %" PRId64 "."
>>                       " dropping connection.",
>>
>
Prasad Pandit Oct. 11, 2018, 12:29 p.m. UTC | #3
+-- On Thu, 11 Oct 2018, Philippe Mathieu-Daudé wrote --+
| The IOReadHandler does not have documentation.
| 
|  typedef void IOReadHandler(void *opaque, const uint8_t *buf, int size);
| 
| Why is the 'size' argument signed? Does it makes sens to call it with a
| negative value?

  No, it doesn't IMO. I had first changed argument type 'int' to uint32_t'.
as

  typedef void IOReadHandler(void *opaque, const uint8_t *buf, uint32_t size);

But 'IOReadHandler' is registered and called from multiple char devices, 
which lead to compile time errors. As the function prototype changed.

I'll update them all, if the above change is okay.

Thank you.
--
Prasad J Pandit / Red Hat Product Security Team
47AF CE69 3A90 54AA 9045 1053 DD13 3D32 FE5B 041F
Paolo Bonzini Oct. 11, 2018, 12:34 p.m. UTC | #4
On 11/10/2018 14:29, P J P wrote:
> +-- On Thu, 11 Oct 2018, Philippe Mathieu-Daudé wrote --+
> | The IOReadHandler does not have documentation.
> | 
> |  typedef void IOReadHandler(void *opaque, const uint8_t *buf, int size);
> | 
> | Why is the 'size' argument signed? Does it makes sens to call it with a
> | negative value?
> 
>   No, it doesn't IMO. I had first changed argument type 'int' to uint32_t'.
> as
> 
>   typedef void IOReadHandler(void *opaque, const uint8_t *buf, uint32_t size);
> 
> But 'IOReadHandler' is registered and called from multiple char devices, 
> which lead to compile time errors. As the function prototype changed.
> 
> I'll update them all, if the above change is okay.

Yes, please do so.

Paolo

> Thank you.
> --
> Prasad J Pandit / Red Hat Product Security Team
> 47AF CE69 3A90 54AA 9045 1053 DD13 3D32 FE5B 041F
>
Philippe Mathieu-Daudé Oct. 11, 2018, 12:38 p.m. UTC | #5
On 11/10/2018 14:29, P J P wrote:
> +-- On Thu, 11 Oct 2018, Philippe Mathieu-Daudé wrote --+
> | The IOReadHandler does not have documentation.
> | 
> |  typedef void IOReadHandler(void *opaque, const uint8_t *buf, int size);
> | 
> | Why is the 'size' argument signed? Does it makes sens to call it with a
> | negative value?
> 
>   No, it doesn't IMO. I had first changed argument type 'int' to uint32_t'.
> as
> 
>   typedef void IOReadHandler(void *opaque, const uint8_t *buf, uint32_t size);
> 
> But 'IOReadHandler' is registered and called from multiple char devices, 
> which lead to compile time errors. As the function prototype changed.
> 
> I'll update them all, if the above change is okay.

I started this change and already converted 40 files.
Prasad Pandit Oct. 11, 2018, 12:50 p.m. UTC | #6
+-- On Thu, 11 Oct 2018, Philippe Mathieu-Daudé wrote --+
| I started this change and already converted 40 files.

Wow, that's super swift! :) Will wait for the patch V2 from you then.

Thank you.
--
Prasad J Pandit / Red Hat Product Security Team
47AF CE69 3A90 54AA 9045 1053 DD13 3D32 FE5B 041F
diff mbox series

Patch

diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c
index 0a6c657228..63ed78f4c6 100644
--- a/hw/usb/ccid-card-passthru.c
+++ b/hw/usb/ccid-card-passthru.c
@@ -275,6 +275,7 @@  static void ccid_card_vscard_read(void *opaque, const uint8_t *buf, int size)
     PassthruState *card = opaque;
     VSCMsgHeader *hdr;
 
+    assert(0 <= size && size < VSCARD_IN_SIZE);
     if (card->vscard_in_pos + size > VSCARD_IN_SIZE) {
         error_report("no room for data: pos %u +  size %d > %" PRId64 "."
                      " dropping connection.",