diff mbox

[3/4] thinkpad-acpi: Avoid heap buffer overrun

Message ID 1249139060-15392-4-git-send-email-hmh@hmh.eng.br (mailing list archive)
State Accepted
Headers show

Commit Message

Henrique de Moraes Holschuh Aug. 1, 2009, 3:04 p.m. UTC
From: Michael Buesch <mb@bu3sch.de>

Avoid a heap buffer overrun triggered by an integer overflow of the
userspace controlled "count" variable.

If userspace passes in a "count" of (size_t)-1l, the kmalloc size will
overflow to ((size_t)-1l + 2) = 1, so only one byte will be allocated.
However, copy_from_user() will attempt to copy 0xFFFFFFFF (or
0xFFFFFFFFFFFFFFFF on 64bit) bytes to the buffer.

A possible testcase could look like this:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
	int fd;
	char c;

	if (argc != 2) {
		printf("Usage: %s /proc/acpi/ibm/filename\n", argv[0]);
		return 1;
	}
	fd = open(argv[1], O_RDWR);
	if (fd < 0) {
		printf("Could not open proc file\n");
		return 1;
	}
	write(fd, &c, (size_t)-1l);
}

We avoid the integer overrun by putting an arbitrary limit on the count.
PAGE_SIZE sounds like a sane limit.

(note: this bug exists at least since kernel 2.6.12...)

Signed-off-by: Michael Buesch <mb@bu3sch.de>
Acked-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Cc: stable@kernel.org
---
 drivers/platform/x86/thinkpad_acpi.c |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

Comments

Michael Buesch Aug. 1, 2009, 3:54 p.m. UTC | #1
On Saturday 01 August 2009 17:04:19 Henrique de Moraes Holschuh wrote:
> From: Michael Buesch <mb@bu3sch.de>
> 
> Avoid a heap buffer overrun triggered by an integer overflow of the
> userspace controlled "count" variable.
> 
> If userspace passes in a "count" of (size_t)-1l, the kmalloc size will
> overflow to ((size_t)-1l + 2) = 1, so only one byte will be allocated.
> However, copy_from_user() will attempt to copy 0xFFFFFFFF (or
> 0xFFFFFFFFFFFFFFFF on 64bit) bytes to the buffer.
> 
> A possible testcase could look like this:
> 
> #include <stdio.h>
> #include <stdlib.h>
> #include <unistd.h>
> #include <fcntl.h>
> 
> int main(int argc, char **argv)
> {
> 	int fd;
> 	char c;
> 
> 	if (argc != 2) {
> 		printf("Usage: %s /proc/acpi/ibm/filename\n", argv[0]);
> 		return 1;
> 	}
> 	fd = open(argv[1], O_RDWR);
> 	if (fd < 0) {
> 		printf("Could not open proc file\n");
> 		return 1;
> 	}
> 	write(fd, &c, (size_t)-1l);
> }
> 
> We avoid the integer overrun by putting an arbitrary limit on the count.
> PAGE_SIZE sounds like a sane limit.
> 
> (note: this bug exists at least since kernel 2.6.12...)
> 
> Signed-off-by: Michael Buesch <mb@bu3sch.de>
> Acked-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
> Cc: stable@kernel.org
> ---
>  drivers/platform/x86/thinkpad_acpi.c |    2 ++
>  1 files changed, 2 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
> index 27d68e7..18f9ee6 100644
> --- a/drivers/platform/x86/thinkpad_acpi.c
> +++ b/drivers/platform/x86/thinkpad_acpi.c
> @@ -766,6 +766,8 @@ static int dispatch_procfs_write(struct file *file,
>  
>  	if (!ibm || !ibm->write)
>  		return -EINVAL;
> +	if (count > PAGE_SIZE - 2)
> +		return -EINVAL;
>  
>  	kernbuf = kmalloc(count + 2, GFP_KERNEL);
>  	if (!kernbuf)

Note that it turns out this is not a real-life bug after all.
The VFS code checks count for signedness (high bit set) and bails
out if this is the case.
Well, it might probably be a good idea to restrict the count range to
something sane here anyway, so...
Henrique de Moraes Holschuh Aug. 2, 2009, 1:50 a.m. UTC | #2
On Sat, 01 Aug 2009, Michael Buesch wrote:
> On Saturday 01 August 2009 17:04:19 Henrique de Moraes Holschuh wrote:
> > From: Michael Buesch <mb@bu3sch.de>
> > 
> > Avoid a heap buffer overrun triggered by an integer overflow of the
> > userspace controlled "count" variable.
> > 
> > If userspace passes in a "count" of (size_t)-1l, the kmalloc size will
> > overflow to ((size_t)-1l + 2) = 1, so only one byte will be allocated.
> > However, copy_from_user() will attempt to copy 0xFFFFFFFF (or
> > 0xFFFFFFFFFFFFFFFF on 64bit) bytes to the buffer.
> > 
> > A possible testcase could look like this:
> > 
> > #include <stdio.h>
> > #include <stdlib.h>
> > #include <unistd.h>
> > #include <fcntl.h>
> > 
> > int main(int argc, char **argv)
> > {
> > 	int fd;
> > 	char c;
> > 
> > 	if (argc != 2) {
> > 		printf("Usage: %s /proc/acpi/ibm/filename\n", argv[0]);
> > 		return 1;
> > 	}
> > 	fd = open(argv[1], O_RDWR);
> > 	if (fd < 0) {
> > 		printf("Could not open proc file\n");
> > 		return 1;
> > 	}
> > 	write(fd, &c, (size_t)-1l);
> > }
> > 
> > We avoid the integer overrun by putting an arbitrary limit on the count.
> > PAGE_SIZE sounds like a sane limit.
> > 
> > (note: this bug exists at least since kernel 2.6.12...)
> > 
> > Signed-off-by: Michael Buesch <mb@bu3sch.de>
> > Acked-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
> > Cc: stable@kernel.org
> > ---
> >  drivers/platform/x86/thinkpad_acpi.c |    2 ++
> >  1 files changed, 2 insertions(+), 0 deletions(-)
> > 
> > diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
> > index 27d68e7..18f9ee6 100644
> > --- a/drivers/platform/x86/thinkpad_acpi.c
> > +++ b/drivers/platform/x86/thinkpad_acpi.c
> > @@ -766,6 +766,8 @@ static int dispatch_procfs_write(struct file *file,
> >  
> >  	if (!ibm || !ibm->write)
> >  		return -EINVAL;
> > +	if (count > PAGE_SIZE - 2)
> > +		return -EINVAL;
> >  
> >  	kernbuf = kmalloc(count + 2, GFP_KERNEL);
> >  	if (!kernbuf)
> 
> Note that it turns out this is not a real-life bug after all.
> The VFS code checks count for signedness (high bit set) and bails
> out if this is the case.
> Well, it might probably be a good idea to restrict the count range to
> something sane here anyway, so...

It is a good idea to restrict the maximum size to something sane anyway.

But the commit message needs to be fixed if there is no security hole.

Anyway, in which function and file is the VFS code that restricts the
maximum size?
Len Brown Aug. 2, 2009, 4:11 a.m. UTC | #3
applied w/ simplified check-in commment

thanks,
Len Brown, Intel Open Source Technology Center


--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Michael Buesch Aug. 2, 2009, 9:53 a.m. UTC | #4
On Sunday 02 August 2009 03:50:12 Henrique de Moraes Holschuh wrote:
> > Note that it turns out this is not a real-life bug after all.
> > The VFS code checks count for signedness (high bit set) and bails
> > out if this is the case.
> > Well, it might probably be a good idea to restrict the count range to
> > something sane here anyway, so...
> 
> It is a good idea to restrict the maximum size to something sane anyway.
> 
> But the commit message needs to be fixed if there is no security hole.
> 
> Anyway, in which function and file is the VFS code that restricts the
> maximum size?
> 

Well there are two things. It happens that access_ok() on x86 does such a
check. It does _not_ do this on all arches, but as a thinkpad is x86...
And there are sanity checks in fs/read_write.c:

208 /*
209  * rw_verify_area doesn't like huge counts. We limit
210  * them to something that fits in "int" so that others
211  * won't have to do range checks all the time.
212  */
213 #define MAX_RW_COUNT (INT_MAX & PAGE_CACHE_MASK)
214 
215 int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count)
216 {
217         struct inode *inode;
218         loff_t pos;
219         int retval = -EINVAL;
220 
221         inode = file->f_path.dentry->d_inode;
222         if (unlikely((ssize_t) count < 0))
223                 return retval;
224         pos = *ppos;
225         if (unlikely((pos < 0) || (loff_t) (pos + count) < 0))
226                 return retval;
227 
228         if (unlikely(inode->i_flock && mandatory_lock(inode))) {
229                 retval = locks_mandatory_area(
230                         read_write == READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE,
231                         inode, file, pos, count);
232                 if (retval < 0)
233                         return retval;
234         }
235         retval = security_file_permission(file,
236                                 read_write == READ ? MAY_READ : MAY_WRITE);
237         if (retval)
238                 return retval;
239         return count > MAX_RW_COUNT ? MAX_RW_COUNT : count;
240 }
Michael Buesch Aug. 2, 2009, 9:54 a.m. UTC | #5
On Sunday 02 August 2009 06:11:13 Len Brown wrote:
> applied w/ simplified check-in commment
> 
> thanks,
> Len Brown, Intel Open Source Technology Center

Thanks.
The same discussion applies to the toshiba_acpi patch I sent to you.
diff mbox

Patch

diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index 27d68e7..18f9ee6 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -766,6 +766,8 @@  static int dispatch_procfs_write(struct file *file,
 
 	if (!ibm || !ibm->write)
 		return -EINVAL;
+	if (count > PAGE_SIZE - 2)
+		return -EINVAL;
 
 	kernbuf = kmalloc(count + 2, GFP_KERNEL);
 	if (!kernbuf)