pf: Improve input validation

If we pass an anchor name which doesn't exist pfr_table_count() returns
-1, which leads to an overflow in mallocarray() and thus a panic.

Explicitly check that pfr_table_count() does not return an error.

Reported-by:	syzbot+bd09d55d897d63d5f4f4@syzkaller.appspotmail.com
Reviewed by:	melifaro
MFC after:	1 week
Differential Revision:	https://reviews.freebsd.org/D24539
This commit is contained in:
Kristof Provost 2020-04-26 16:16:39 +00:00
parent db20acc029
commit a7c8533634
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=360344

View File

@ -3008,7 +3008,8 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
case DIOCRGETTABLES: {
struct pfioc_table *io = (struct pfioc_table *)addr;
struct pfr_table *pfrts;
size_t totlen, n;
size_t totlen;
int n;
if (io->pfrio_esize != sizeof(struct pfr_table)) {
error = ENODEV;
@ -3016,6 +3017,11 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
}
PF_RULES_RLOCK();
n = pfr_table_count(&io->pfrio_table, io->pfrio_flags);
if (n < 0) {
PF_RULES_RUNLOCK();
error = EINVAL;
break;
}
io->pfrio_size = min(io->pfrio_size, n);
totlen = io->pfrio_size * sizeof(struct pfr_table);
@ -3039,7 +3045,8 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
case DIOCRGETTSTATS: {
struct pfioc_table *io = (struct pfioc_table *)addr;
struct pfr_tstats *pfrtstats;
size_t totlen, n;
size_t totlen;
int n;
if (io->pfrio_esize != sizeof(struct pfr_tstats)) {
error = ENODEV;
@ -3047,6 +3054,11 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
}
PF_RULES_WLOCK();
n = pfr_table_count(&io->pfrio_table, io->pfrio_flags);
if (n < 0) {
PF_RULES_WUNLOCK();
error = EINVAL;
break;
}
io->pfrio_size = min(io->pfrio_size, n);
totlen = io->pfrio_size * sizeof(struct pfr_tstats);
@ -3069,7 +3081,8 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
case DIOCRCLRTSTATS: {
struct pfioc_table *io = (struct pfioc_table *)addr;
struct pfr_table *pfrts;
size_t totlen, n;
size_t totlen;
int n;
if (io->pfrio_esize != sizeof(struct pfr_table)) {
error = ENODEV;
@ -3078,6 +3091,11 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
PF_RULES_WLOCK();
n = pfr_table_count(&io->pfrio_table, io->pfrio_flags);
if (n < 0) {
PF_RULES_WUNLOCK();
error = EINVAL;
break;
}
io->pfrio_size = min(io->pfrio_size, n);
totlen = io->pfrio_size * sizeof(struct pfr_table);
@ -3104,7 +3122,8 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
case DIOCRSETTFLAGS: {
struct pfioc_table *io = (struct pfioc_table *)addr;
struct pfr_table *pfrts;
size_t totlen, n;
size_t totlen;
int n;
if (io->pfrio_esize != sizeof(struct pfr_table)) {
error = ENODEV;
@ -3113,6 +3132,12 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
PF_RULES_RLOCK();
n = pfr_table_count(&io->pfrio_table, io->pfrio_flags);
if (n < 0) {
PF_RULES_RUNLOCK();
error = EINVAL;
break;
}
io->pfrio_size = min(io->pfrio_size, n);
PF_RULES_RUNLOCK();