diff mbox

ALSA: asihpi: fix an information leak in asihpi_hpi_ioctl()

Message ID 20141222074946.GA9737@mwanda (mailing list archive)
State New, archived
Headers show

Commit Message

Dan Carpenter Dec. 22, 2014, 7:49 a.m. UTC
So far as I can see "hr->h.size" is set to "res_max_size" which is a
user controlled value between 12 and USHRT_MAX.  If it's larger than
sizeof(*hr), then that leads to an information leak.

I am not very familiar with this code, my other question here is that
on lines before we set "hr->h.size = sizeof(hr->h)".  It think this is
a bug.  I also think this particular code is never executed and I added
a comment to that effect.  But we do it in earlier in the function as
well:

	copy_to_user(puhr, hr, sizeof(hr->h));

It doesn't make sense to me.

Anyway, I think my patch is safe and it seems to fix a real information
leak.

Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>

Comments

Takashi Iwai Dec. 26, 2014, 11:07 a.m. UTC | #1
At Mon, 22 Dec 2014 10:49:46 +0300,
Dan Carpenter wrote:
> 
> So far as I can see "hr->h.size" is set to "res_max_size" which is a
> user controlled value between 12 and USHRT_MAX.  If it's larger than
> sizeof(*hr), then that leads to an information leak.
> 
> I am not very familiar with this code, my other question here is that
> on lines before we set "hr->h.size = sizeof(hr->h)".  It think this is
> a bug.  I also think this particular code is never executed and I added
> a comment to that effect.  But we do it in earlier in the function as
> well:
> 
> 	copy_to_user(puhr, hr, sizeof(hr->h));
> 
> It doesn't make sense to me.
> 
> Anyway, I think my patch is safe and it seems to fix a real information
> leak.
>
> Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
> 
> diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c
> index 6aa677e..f88109a 100644
> --- a/sound/pci/asihpi/hpioctl.c
> +++ b/sound/pci/asihpi/hpioctl.c
> @@ -283,6 +283,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>  		goto out;
>  	}
>  
> +	/* FIXME:  isn't this a no-op? */

The whole union member may be overwritten by a response.

>  	if (hr->h.size > res_max_size) {
>  		HPI_DEBUG_LOG(ERROR, "response too big %d %d\n", hr->h.size,
>  			res_max_size);
> @@ -290,6 +291,9 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>  		hr->h.specific_error = hr->h.size;
>  		hr->h.size = sizeof(hr->h);
>  	}
> +	/* prevent an information leak */
> +	if (hr->h.size > sizeof(*hr))
> +		hr->h.size = sizeof(*hr);

Checking the size is good, but there is already a check against
res_max_size.  So, we'd rather need to check res_max_size itself
whether it's in a sane range.  The more fitting place would be at the
beginning of the function where it checks already res_max_size <
sizeof(struct hpi_response_header)).


thanks,

Takashi

>  
>  	uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
>  	if (uncopied_bytes) {
>
Eliot Blennerhassett Dec. 30, 2014, 8:07 a.m. UTC | #2
On 27/12/14 00:07, Takashi Iwai wrote:
> At Mon, 22 Dec 2014 10:49:46 +0300,
> Dan Carpenter wrote:
>>
>> So far as I can see "hr->h.size" is set to "res_max_size" which is a
>> user controlled value between 12 and USHRT_MAX.  If it's larger than
>> sizeof(*hr), then that leads to an information leak.
>>
>> I am not very familiar with this code, my other question here is that
>> on lines before we set "hr->h.size = sizeof(hr->h)".  It think this is
>> a bug.  I also think this particular code is never executed and I added
>> a comment to that effect.  But we do it in earlier in the function as
>> well:
>>
>> 	copy_to_user(puhr, hr, sizeof(hr->h));
>>
>> It doesn't make sense to me.
>>
>> Anyway, I think my patch is safe and it seems to fix a real information
>> leak.
>>
>> Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
>>
>> diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c
>> index 6aa677e..f88109a 100644
>> --- a/sound/pci/asihpi/hpioctl.c
>> +++ b/sound/pci/asihpi/hpioctl.c
>> @@ -283,6 +283,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>>  		goto out;
>>  	}
>>  
>> +	/* FIXME:  isn't this a no-op? */
> 
> The whole union member may be overwritten by a response.
> 
>>  	if (hr->h.size > res_max_size) {
>>  		HPI_DEBUG_LOG(ERROR, "response too big %d %d\n", hr->h.size,
>>  			res_max_size);
>> @@ -290,6 +291,9 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>>  		hr->h.specific_error = hr->h.size;
>>  		hr->h.size = sizeof(hr->h);
>>  	}
>> +	/* prevent an information leak */
>> +	if (hr->h.size > sizeof(*hr))
>> +		hr->h.size = sizeof(*hr);
> 
> Checking the size is good, but there is already a check against
> res_max_size.  So, we'd rather need to check res_max_size itself
> whether it's in a sane range.  The more fitting place would be at the
> beginning of the function where it checks already res_max_size <
> sizeof(struct hpi_response_header)).

Yes.

At that point res_max_size is size of userspace buffer, and *hr is
effectively kernelspace buffer,  so take lowest of the two.

i.e.
    res_max_size = min(res_max_size, sizeof(*hr));

On the way "down" from here hr->h.size is available buffer size for a
response from card DSP, then on the way "up", hr->h.size is the actual
size of the response.  This value comes from DSP by DMA or IO read.

Which leads me to notice another problem in hpi6000.c, where the
response read from DSP is not limited to the given buffer size.

I will follow up with a patch in the next few days.

> 
> 
> thanks,
> 
> Takashi
> 
>>  
>>  	uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
>>  	if (uncopied_bytes) {
>>
diff mbox

Patch

diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c
index 6aa677e..f88109a 100644
--- a/sound/pci/asihpi/hpioctl.c
+++ b/sound/pci/asihpi/hpioctl.c
@@ -283,6 +283,7 @@  long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 		goto out;
 	}
 
+	/* FIXME:  isn't this a no-op? */
 	if (hr->h.size > res_max_size) {
 		HPI_DEBUG_LOG(ERROR, "response too big %d %d\n", hr->h.size,
 			res_max_size);
@@ -290,6 +291,9 @@  long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 		hr->h.specific_error = hr->h.size;
 		hr->h.size = sizeof(hr->h);
 	}
+	/* prevent an information leak */
+	if (hr->h.size > sizeof(*hr))
+		hr->h.size = sizeof(*hr);
 
 	uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
 	if (uncopied_bytes) {