- don't return ENOMEM for errors not related to memory

- don't overload return values (-error/+size)
- don't allocate kernel memory from user supplied length.
This commit is contained in:
christos 2018-01-28 22:24:58 +00:00
parent 8982bc4889
commit 96bd66bcf0
1 changed files with 37 additions and 52 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: subr_interrupt.c,v 1.3 2018/01/13 13:53:36 reinoud Exp $ */ /* $NetBSD: subr_interrupt.c,v 1.4 2018/01/28 22:24:58 christos Exp $ */
/* /*
* Copyright (c) 2015 Internet Initiative Japan Inc. * Copyright (c) 2015 Internet Initiative Japan Inc.
@ -27,7 +27,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: subr_interrupt.c,v 1.3 2018/01/13 13:53:36 reinoud Exp $"); __KERNEL_RCSID(0, "$NetBSD: subr_interrupt.c,v 1.4 2018/01/28 22:24:58 christos Exp $");
#include <sys/param.h> #include <sys/param.h>
#include <sys/systm.h> #include <sys/systm.h>
@ -133,7 +133,7 @@ interrupt_avert_intr(u_int cpu_idx)
ii_handler = interrupt_construct_intrids(cpuset); ii_handler = interrupt_construct_intrids(cpuset);
if (ii_handler == NULL) { if (ii_handler == NULL) {
error = ENOMEM; error = EINVAL;
goto out; goto out;
} }
nids = ii_handler->iih_nids; nids = ii_handler->iih_nids;
@ -180,25 +180,24 @@ interrupt_intrio_list_line_size(void)
* Return the size of interrupts list data on success. * Return the size of interrupts list data on success.
* Reterun 0 on failed. * Reterun 0 on failed.
*/ */
static size_t static int
interrupt_intrio_list_size(void) interrupt_intrio_list_size(size_t *ilsize)
{ {
struct intrids_handler *ii_handler; struct intrids_handler *ii_handler;
size_t ilsize;
ilsize = 0; *ilsize = 0;
/* buffer header */ /* buffer header */
ilsize += sizeof(struct intrio_list); *ilsize += sizeof(struct intrio_list);
/* il_line body */ /* il_line body */
ii_handler = interrupt_construct_intrids(kcpuset_running); ii_handler = interrupt_construct_intrids(kcpuset_running);
if (ii_handler == NULL) if (ii_handler == NULL)
return 0; return EOPNOTSUPP;
ilsize += interrupt_intrio_list_line_size() * (ii_handler->iih_nids); *ilsize += interrupt_intrio_list_line_size() * ii_handler->iih_nids;
interrupt_destruct_intrids(ii_handler); interrupt_destruct_intrids(ii_handler);
return ilsize; return 0;
} }
/* /*
@ -207,28 +206,17 @@ interrupt_intrio_list_size(void)
* If "data" == NULL, simply return list structure bytes. * If "data" == NULL, simply return list structure bytes.
*/ */
static int static int
interrupt_intrio_list(struct intrio_list *il, int length) interrupt_intrio_list(struct intrio_list *il, size_t ilsize)
{ {
struct intrio_list_line *illine; struct intrio_list_line *illine;
kcpuset_t *assigned, *avail; kcpuset_t *assigned, *avail;
struct intrids_handler *ii_handler; struct intrids_handler *ii_handler;
intrid_t *ids; intrid_t *ids;
size_t ilsize;
u_int cpu_idx; u_int cpu_idx;
int nids, intr_idx, ret, line_size; int nids, intr_idx, error, line_size;
ilsize = interrupt_intrio_list_size();
if (ilsize == 0)
return -ENOMEM;
if (il == NULL)
return ilsize;
if (length < ilsize)
return -ENOMEM;
illine = (struct intrio_list_line *) illine = (struct intrio_list_line *)
((char *)il + sizeof(struct intrio_list)); ((char *)il + sizeof(struct intrio_list));
il->il_lineoffset = (off_t)((uintptr_t)illine - (uintptr_t)il); il->il_lineoffset = (off_t)((uintptr_t)illine - (uintptr_t)il);
kcpuset_create(&avail, true); kcpuset_create(&avail, true);
@ -238,19 +226,19 @@ interrupt_intrio_list(struct intrio_list *il, int length)
ii_handler = interrupt_construct_intrids(kcpuset_running); ii_handler = interrupt_construct_intrids(kcpuset_running);
if (ii_handler == NULL) { if (ii_handler == NULL) {
DPRINTF(("%s: interrupt_construct_intrids() failed\n", DPRINTF(("%s: interrupt_construct_intrids() failed\n",
__func__)); __func__));
ret = -ENOMEM; error = EOPNOTSUPP;
goto out; goto out;
} }
line_size = interrupt_intrio_list_line_size(); line_size = interrupt_intrio_list_line_size();
/* ensure interrupts are not added after interrupt_intrio_list_size(). */ /* ensure interrupts are not added after interrupt_intrio_list_size() */
nids = ii_handler->iih_nids; nids = ii_handler->iih_nids;
ids = ii_handler->iih_intrids; ids = ii_handler->iih_intrids;
if (ilsize < sizeof(struct intrio_list) + line_size * nids) { if (ilsize < sizeof(struct intrio_list) + line_size * nids) {
DPRINTF(("%s: interrupts are added during execution.\n", DPRINTF(("%s: interrupts are added during execution.\n",
__func__)); __func__));
ret = -ENOMEM; error = EAGAIN;
goto destruct_out; goto destruct_out;
} }
@ -264,19 +252,19 @@ interrupt_intrio_list(struct intrio_list *il, int length)
interrupt_get_assigned(ids[intr_idx], assigned); interrupt_get_assigned(ids[intr_idx], assigned);
for (cpu_idx = 0; cpu_idx < ncpu; cpu_idx++) { for (cpu_idx = 0; cpu_idx < ncpu; cpu_idx++) {
struct intrio_list_line_cpu *illcpu = struct intrio_list_line_cpu *illcpu =
&illine->ill_cpu[cpu_idx]; &illine->ill_cpu[cpu_idx];
illcpu->illc_assigned = illcpu->illc_assigned =
kcpuset_isset(assigned, cpu_idx) ? true : false; kcpuset_isset(assigned, cpu_idx);
illcpu->illc_count = illcpu->illc_count =
interrupt_get_count(ids[intr_idx], cpu_idx); interrupt_get_count(ids[intr_idx], cpu_idx);
} }
illine = (struct intrio_list_line *) illine = (struct intrio_list_line *)
((char *)illine + line_size); ((char *)illine + line_size);
} }
ret = ilsize; error = 0;
il->il_version = INTRIO_LIST_VERSION; il->il_version = INTRIO_LIST_VERSION;
il->il_ncpus = ncpu; il->il_ncpus = ncpu;
il->il_nintrs = nids; il->il_nintrs = nids;
@ -289,7 +277,7 @@ interrupt_intrio_list(struct intrio_list *il, int length)
kcpuset_destroy(assigned); kcpuset_destroy(assigned);
kcpuset_destroy(avail); kcpuset_destroy(avail);
return ret; return error;
} }
/* /*
@ -298,42 +286,39 @@ interrupt_intrio_list(struct intrio_list *il, int length)
static int static int
interrupt_intrio_list_sysctl(SYSCTLFN_ARGS) interrupt_intrio_list_sysctl(SYSCTLFN_ARGS)
{ {
int ret, error; int error;
void *buf; void *buf;
size_t ilsize;
if (oldlenp == NULL) if (oldlenp == NULL)
return EINVAL; return EINVAL;
if ((error = interrupt_intrio_list_size(&ilsize)) != 0)
return error;
/* /*
* If oldp == NULL, the sysctl(8) caller process want to get the size of * If oldp == NULL, the sysctl(8) caller process want to get the size of
* intrctl list data only. * intrctl list data only.
*/ */
if (oldp == NULL) { if (oldp == NULL) {
ret = interrupt_intrio_list(NULL, 0); *oldlenp = ilsize;
if (ret < 0)
return -ret;
*oldlenp = ret;
return 0; return 0;
} }
/* /*
* If oldp != NULL, the sysctl(8) caller process want to get both the size * If oldp != NULL, the sysctl(8) caller process want to get both the
* and the contents of intrctl list data. * size and the contents of intrctl list data.
*/ */
if (*oldlenp == 0) if (*oldlenp < ilsize)
return ENOMEM; return ENOMEM;
buf = kmem_zalloc(*oldlenp, KM_SLEEP); buf = kmem_zalloc(ilsize, KM_SLEEP);
ret = interrupt_intrio_list(buf, *oldlenp); if ((error = interrupt_intrio_list(buf, ilsize)) != 0)
if (ret < 0) {
error = -ret;
goto out; goto out;
}
error = copyout(buf, oldp, *oldlenp);
error = copyout(buf, oldp, ilsize);
out: out:
kmem_free(buf, *oldlenp); kmem_free(buf, ilsize);
return error; return error;
} }