summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpjd <pjd@FreeBSD.org>2005-06-11 14:58:20 +0000
committerpjd <pjd@FreeBSD.org>2005-06-11 14:58:20 +0000
commitbe79126844179d84dda297cece04bb6d2462eb03 (patch)
treeba08cbe1479ab6475a323dcd20c59c880dafee4d
parente5e29d142df527fb60c2bf5787693048c03ccd91 (diff)
downloadFreeBSD-src-be79126844179d84dda297cece04bb6d2462eb03.zip
FreeBSD-src-be79126844179d84dda297cece04bb6d2462eb03.tar.gz
Do not allocate memory based on not-checked argument from userland.
It can be used to panic the kernel by giving too big value. Fix it by moving allocation and size verification into kern_getfsstat(). This even simplifies kern_getfsstat() consumers, but destroys symmetry - memory is allocated inside kern_getfsstat(), but has to be freed by the caller. Found by: FreeBSD Kernel Stress Test Suite: http://www.holm.cc/stress/ Reported by: Peter Holm <peter@holm.cc>
-rw-r--r--sys/alpha/osf1/osf1_mount.c8
-rw-r--r--sys/compat/freebsd32/freebsd32_misc.c8
-rw-r--r--sys/kern/vfs_extattr.c41
-rw-r--r--sys/kern/vfs_syscalls.c41
-rw-r--r--sys/sys/syscallsubr.h2
5 files changed, 61 insertions, 39 deletions
diff --git a/sys/alpha/osf1/osf1_mount.c b/sys/alpha/osf1/osf1_mount.c
index 05c91ff..d0ef281 100644
--- a/sys/alpha/osf1/osf1_mount.c
+++ b/sys/alpha/osf1/osf1_mount.c
@@ -170,12 +170,8 @@ osf1_getfsstat(td, uap)
count = uap->bufsize / sizeof(struct osf1_statfs);
size = count * sizeof(struct statfs);
- if (size > 0)
- buf = malloc(size, M_TEMP, M_WAITOK);
- else
- buf = NULL;
- error = kern_getfsstat(td, buf, size, UIO_SYSSPACE, flags);
- if (buf != NULL) {
+ error = kern_getfsstat(td, &buf, size, UIO_SYSSPACE, flags);
+ if (size > 0) {
count = td->td_retval[0];
sp = buf;
while (count > 0 && error == 0) {
diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c
index 1c003a0..ae61fbd 100644
--- a/sys/compat/freebsd32/freebsd32_misc.c
+++ b/sys/compat/freebsd32/freebsd32_misc.c
@@ -163,12 +163,8 @@ freebsd4_freebsd32_getfsstat(struct thread *td, struct freebsd4_freebsd32_getfss
count = uap->bufsize / sizeof(struct statfs32);
size = count * sizeof(struct statfs);
- if (size > 0)
- buf = malloc(size, M_TEMP, M_WAITOK);
- else
- buf = NULL;
- error = kern_getfsstat(td, buf, size, UIO_SYSSPACE, uap->flags);
- if (buf != NULL) {
+ error = kern_getfsstat(td, &buf, size, UIO_SYSSPACE, uap->flags);
+ if (size > 0) {
count = td->td_retval[0];
sp = buf;
while (count > 0 && error == 0) {
diff --git a/sys/kern/vfs_extattr.c b/sys/kern/vfs_extattr.c
index 44ed4bf..242473f 100644
--- a/sys/kern/vfs_extattr.c
+++ b/sys/kern/vfs_extattr.c
@@ -390,12 +390,17 @@ getfsstat(td, uap)
} */ *uap;
{
- return (kern_getfsstat(td, uap->buf, uap->bufsize, UIO_USERSPACE,
+ return (kern_getfsstat(td, &uap->buf, uap->bufsize, UIO_USERSPACE,
uap->flags));
}
+/*
+ * If (bufsize > 0 && bufseg == UIO_SYSSPACE)
+ * The caller is responsible for freeing memory which will be allocated
+ * in '*buf'.
+ */
int
-kern_getfsstat(struct thread *td, struct statfs *buf, size_t bufsize,
+kern_getfsstat(struct thread *td, struct statfs **buf, size_t bufsize,
enum uio_seg bufseg, int flags)
{
struct mount *mp, *nmp;
@@ -404,10 +409,23 @@ kern_getfsstat(struct thread *td, struct statfs *buf, size_t bufsize,
int error;
maxcount = bufsize / sizeof(struct statfs);
- sfsp = buf;
- count = 0;
mtx_lock(&Giant);
mtx_lock(&mountlist_mtx);
+ if (bufsize == 0)
+ sfsp = NULL;
+ else if (bufseg == UIO_USERSPACE)
+ sfsp = *buf;
+ else /* if (bufseg == UIO_SYSSPACE) */ {
+ count = 0;
+ TAILQ_FOREACH(mp, &mountlist, mnt_list) {
+ count++;
+ }
+ if (maxcount > count)
+ maxcount = count;
+ sfsp = *buf = malloc(maxcount * sizeof(struct statfs), M_TEMP,
+ M_WAITOK);
+ }
+ count = 0;
for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) {
if (prison_canseemount(td->td_ucred, mp) != 0) {
nmp = TAILQ_NEXT(mp, mnt_list);
@@ -451,15 +469,16 @@ kern_getfsstat(struct thread *td, struct statfs *buf, size_t bufsize,
prison_enforce_statfs(td->td_ucred, mp, &sb);
sp = &sb;
}
- if (bufseg == UIO_USERSPACE) {
+ if (bufseg == UIO_SYSSPACE)
+ bcopy(sp, sfsp, sizeof(*sp));
+ else /* if (bufseg == UIO_USERSPACE) */ {
error = copyout(sp, sfsp, sizeof(*sp));
if (error) {
vfs_unbusy(mp, td);
mtx_unlock(&Giant);
return (error);
}
- } else
- bcopy(sp, sfsp, sizeof(*sp));
+ }
sfsp++;
}
count++;
@@ -561,12 +580,8 @@ freebsd4_getfsstat(td, uap)
count = uap->bufsize / sizeof(struct ostatfs);
size = count * sizeof(struct statfs);
- if (size > 0)
- buf = malloc(size, M_TEMP, M_WAITOK);
- else
- buf = NULL;
- error = kern_getfsstat(td, buf, size, UIO_SYSSPACE, uap->flags);
- if (buf != NULL) {
+ error = kern_getfsstat(td, &buf, size, UIO_SYSSPACE, uap->flags);
+ if (size > 0) {
count = td->td_retval[0];
sp = buf;
while (count > 0 && error == 0) {
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
index 44ed4bf..242473f 100644
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -390,12 +390,17 @@ getfsstat(td, uap)
} */ *uap;
{
- return (kern_getfsstat(td, uap->buf, uap->bufsize, UIO_USERSPACE,
+ return (kern_getfsstat(td, &uap->buf, uap->bufsize, UIO_USERSPACE,
uap->flags));
}
+/*
+ * If (bufsize > 0 && bufseg == UIO_SYSSPACE)
+ * The caller is responsible for freeing memory which will be allocated
+ * in '*buf'.
+ */
int
-kern_getfsstat(struct thread *td, struct statfs *buf, size_t bufsize,
+kern_getfsstat(struct thread *td, struct statfs **buf, size_t bufsize,
enum uio_seg bufseg, int flags)
{
struct mount *mp, *nmp;
@@ -404,10 +409,23 @@ kern_getfsstat(struct thread *td, struct statfs *buf, size_t bufsize,
int error;
maxcount = bufsize / sizeof(struct statfs);
- sfsp = buf;
- count = 0;
mtx_lock(&Giant);
mtx_lock(&mountlist_mtx);
+ if (bufsize == 0)
+ sfsp = NULL;
+ else if (bufseg == UIO_USERSPACE)
+ sfsp = *buf;
+ else /* if (bufseg == UIO_SYSSPACE) */ {
+ count = 0;
+ TAILQ_FOREACH(mp, &mountlist, mnt_list) {
+ count++;
+ }
+ if (maxcount > count)
+ maxcount = count;
+ sfsp = *buf = malloc(maxcount * sizeof(struct statfs), M_TEMP,
+ M_WAITOK);
+ }
+ count = 0;
for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) {
if (prison_canseemount(td->td_ucred, mp) != 0) {
nmp = TAILQ_NEXT(mp, mnt_list);
@@ -451,15 +469,16 @@ kern_getfsstat(struct thread *td, struct statfs *buf, size_t bufsize,
prison_enforce_statfs(td->td_ucred, mp, &sb);
sp = &sb;
}
- if (bufseg == UIO_USERSPACE) {
+ if (bufseg == UIO_SYSSPACE)
+ bcopy(sp, sfsp, sizeof(*sp));
+ else /* if (bufseg == UIO_USERSPACE) */ {
error = copyout(sp, sfsp, sizeof(*sp));
if (error) {
vfs_unbusy(mp, td);
mtx_unlock(&Giant);
return (error);
}
- } else
- bcopy(sp, sfsp, sizeof(*sp));
+ }
sfsp++;
}
count++;
@@ -561,12 +580,8 @@ freebsd4_getfsstat(td, uap)
count = uap->bufsize / sizeof(struct ostatfs);
size = count * sizeof(struct statfs);
- if (size > 0)
- buf = malloc(size, M_TEMP, M_WAITOK);
- else
- buf = NULL;
- error = kern_getfsstat(td, buf, size, UIO_SYSSPACE, uap->flags);
- if (buf != NULL) {
+ error = kern_getfsstat(td, &buf, size, UIO_SYSSPACE, uap->flags);
+ if (size > 0) {
count = td->td_retval[0];
sp = buf;
while (count > 0 && error == 0) {
diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h
index c09288e..cb9f84a 100644
--- a/sys/sys/syscallsubr.h
+++ b/sys/sys/syscallsubr.h
@@ -69,7 +69,7 @@ int kern_fstat(struct thread *td, int fd, struct stat *sbp);
int kern_fstatfs(struct thread *td, int fd, struct statfs *buf);
int kern_futimes(struct thread *td, int fd, struct timeval *tptr,
enum uio_seg tptrseg);
-int kern_getfsstat(struct thread *td, struct statfs *buf, size_t bufsize,
+int kern_getfsstat(struct thread *td, struct statfs **buf, size_t bufsize,
enum uio_seg bufseg, int flags);
int kern_getitimer(struct thread *, u_int, struct itimerval *);
int kern_getrusage(struct thread *td, int who, struct rusage *rup);
OpenPOWER on IntegriCloud