pf: Improve ioctl validation for DIOCRGETTABLES, DIOCRGETTSTATS, DIOCRCLRTSTATS and DIOCRSETTFLAGS
These ioctls can process a number of items at a time, which puts us at risk of overflow in mallocarray() and of impossibly large allocations even if we don't overflow. Limit the allocation to required size (or the user allocation, if that's smaller). That does mean we need to do the allocation with the rules lock held (so the number doesn't change while we're doing this), so it can't M_WAITOK. MFC after: 1 week
This commit is contained in:
parent
814a016d28
commit
adfe2f6aff
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=332107
@ -1638,6 +1638,7 @@ void pfr_detach_table(struct pfr_ktable *);
|
||||
int pfr_clr_tables(struct pfr_table *, int *, int);
|
||||
int pfr_add_tables(struct pfr_table *, int, int *, int);
|
||||
int pfr_del_tables(struct pfr_table *, int, int *, int);
|
||||
int pfr_table_count(struct pfr_table *, int);
|
||||
int pfr_get_tables(struct pfr_table *, struct pfr_table *, int *, int);
|
||||
int pfr_get_tstats(struct pfr_table *, struct pfr_tstats *, int *, int);
|
||||
int pfr_clr_tstats(struct pfr_table *, int, int *, int);
|
||||
|
@ -2588,20 +2588,25 @@ 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;
|
||||
size_t totlen, n;
|
||||
|
||||
if (io->pfrio_esize != sizeof(struct pfr_table)) {
|
||||
error = ENODEV;
|
||||
break;
|
||||
}
|
||||
PF_RULES_RLOCK();
|
||||
n = pfr_table_count(&io->pfrio_table, io->pfrio_flags);
|
||||
io->pfrio_size = min(io->pfrio_size, n);
|
||||
|
||||
totlen = io->pfrio_size * sizeof(struct pfr_table);
|
||||
|
||||
pfrts = mallocarray(io->pfrio_size, sizeof(struct pfr_table),
|
||||
M_TEMP, M_WAITOK);
|
||||
if (! pfrts) {
|
||||
M_TEMP, M_NOWAIT);
|
||||
if (pfrts == NULL) {
|
||||
error = ENOMEM;
|
||||
PF_RULES_RUNLOCK();
|
||||
break;
|
||||
}
|
||||
PF_RULES_RLOCK();
|
||||
error = pfr_get_tables(&io->pfrio_table, pfrts,
|
||||
&io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL);
|
||||
PF_RULES_RUNLOCK();
|
||||
@ -2614,20 +2619,24 @@ 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;
|
||||
size_t totlen, n;
|
||||
|
||||
if (io->pfrio_esize != sizeof(struct pfr_tstats)) {
|
||||
error = ENODEV;
|
||||
break;
|
||||
}
|
||||
PF_RULES_WLOCK();
|
||||
n = pfr_table_count(&io->pfrio_table, io->pfrio_flags);
|
||||
io->pfrio_size = min(io->pfrio_size, n);
|
||||
|
||||
totlen = io->pfrio_size * sizeof(struct pfr_tstats);
|
||||
pfrtstats = mallocarray(io->pfrio_size,
|
||||
sizeof(struct pfr_tstats), M_TEMP, M_WAITOK);
|
||||
if (! pfrtstats) {
|
||||
sizeof(struct pfr_tstats), M_TEMP, M_NOWAIT);
|
||||
if (pfrtstats == NULL) {
|
||||
error = ENOMEM;
|
||||
PF_RULES_WUNLOCK();
|
||||
break;
|
||||
}
|
||||
PF_RULES_WLOCK();
|
||||
error = pfr_get_tstats(&io->pfrio_table, pfrtstats,
|
||||
&io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL);
|
||||
PF_RULES_WUNLOCK();
|
||||
@ -2640,25 +2649,31 @@ 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;
|
||||
size_t totlen, n;
|
||||
|
||||
if (io->pfrio_esize != sizeof(struct pfr_table)) {
|
||||
error = ENODEV;
|
||||
break;
|
||||
}
|
||||
|
||||
PF_RULES_WLOCK();
|
||||
n = pfr_table_count(&io->pfrio_table, io->pfrio_flags);
|
||||
io->pfrio_size = min(io->pfrio_size, n);
|
||||
|
||||
totlen = io->pfrio_size * sizeof(struct pfr_table);
|
||||
pfrts = mallocarray(io->pfrio_size, sizeof(struct pfr_table),
|
||||
M_TEMP, M_WAITOK);
|
||||
if (! pfrts) {
|
||||
M_TEMP, M_NOWAIT);
|
||||
if (pfrts == NULL) {
|
||||
error = ENOMEM;
|
||||
PF_RULES_WUNLOCK();
|
||||
break;
|
||||
}
|
||||
error = copyin(io->pfrio_buffer, pfrts, totlen);
|
||||
if (error) {
|
||||
free(pfrts, M_TEMP);
|
||||
PF_RULES_WUNLOCK();
|
||||
break;
|
||||
}
|
||||
PF_RULES_WLOCK();
|
||||
error = pfr_clr_tstats(pfrts, io->pfrio_size,
|
||||
&io->pfrio_nzero, io->pfrio_flags | PFR_FLAG_USERIOCTL);
|
||||
PF_RULES_WUNLOCK();
|
||||
@ -2669,25 +2684,31 @@ 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;
|
||||
size_t totlen, n;
|
||||
|
||||
if (io->pfrio_esize != sizeof(struct pfr_table)) {
|
||||
error = ENODEV;
|
||||
break;
|
||||
}
|
||||
|
||||
PF_RULES_WLOCK();
|
||||
n = pfr_table_count(&io->pfrio_table, io->pfrio_flags);
|
||||
io->pfrio_size = min(io->pfrio_size, n);
|
||||
|
||||
totlen = io->pfrio_size * sizeof(struct pfr_table);
|
||||
pfrts = mallocarray(io->pfrio_size, sizeof(struct pfr_table),
|
||||
M_TEMP, M_WAITOK);
|
||||
if (! pfrts) {
|
||||
M_TEMP, M_NOWAIT);
|
||||
if (pfrts == NULL) {
|
||||
error = ENOMEM;
|
||||
PF_RULES_WUNLOCK();
|
||||
break;
|
||||
}
|
||||
error = copyin(io->pfrio_buffer, pfrts, totlen);
|
||||
if (error) {
|
||||
free(pfrts, M_TEMP);
|
||||
PF_RULES_WUNLOCK();
|
||||
break;
|
||||
}
|
||||
PF_RULES_WLOCK();
|
||||
error = pfr_set_tflags(pfrts, io->pfrio_size,
|
||||
io->pfrio_setflag, io->pfrio_clrflag, &io->pfrio_nchange,
|
||||
&io->pfrio_ndel, io->pfrio_flags | PFR_FLAG_USERIOCTL);
|
||||
|
@ -177,7 +177,6 @@ static struct pfr_ktable
|
||||
*pfr_lookup_table(struct pfr_table *);
|
||||
static void pfr_clean_node_mask(struct pfr_ktable *,
|
||||
struct pfr_kentryworkq *);
|
||||
static int pfr_table_count(struct pfr_table *, int);
|
||||
static int pfr_skip_table(struct pfr_table *,
|
||||
struct pfr_ktable *, int);
|
||||
static struct pfr_kentry
|
||||
@ -1688,7 +1687,7 @@ pfr_fix_anchor(char *anchor)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
int
|
||||
pfr_table_count(struct pfr_table *filter, int flags)
|
||||
{
|
||||
struct pf_ruleset *rs;
|
||||
|
Loading…
Reference in New Issue
Block a user