Don't kmem_alloc()/kmem_free() with spin lock held: call can_pcbsetfilter()

without canp_mtx; take it here and check canp_state before updating the
canp_filters.
This commit is contained in:
bouyer 2019-07-20 15:34:41 +00:00
parent 4b86db61e8
commit 8129437f91
2 changed files with 25 additions and 14 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: can.c,v 1.6 2018/11/15 10:23:56 maxv Exp $ */
/* $NetBSD: can.c,v 1.7 2019/07/20 15:34:41 bouyer Exp $ */
/*-
* Copyright (c) 2003, 2017 The NetBSD Foundation, Inc.
@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: can.c,v 1.6 2018/11/15 10:23:56 maxv Exp $");
__KERNEL_RCSID(0, "$NetBSD: can.c,v 1.7 2019/07/20 15:34:41 bouyer Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -924,9 +924,7 @@ can_raw_setop(struct canpcb *canp, struct sockopt *sopt)
int nfilters = sopt->sopt_size / sizeof(struct can_filter);
if (sopt->sopt_size % sizeof(struct can_filter) != 0)
return EINVAL;
mutex_enter(&canp->canp_mtx);
error = can_pcbsetfilter(canp, sopt->sopt_data, nfilters);
mutex_exit(&canp->canp_mtx);
break;
}
default:

View File

@ -1,4 +1,4 @@
/* $NetBSD: can_pcb.c,v 1.7 2019/02/25 06:49:44 maxv Exp $ */
/* $NetBSD: can_pcb.c,v 1.8 2019/07/20 15:34:41 bouyer Exp $ */
/*-
* Copyright (c) 2003, 2017 The NetBSD Foundation, Inc.
@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: can_pcb.c,v 1.7 2019/02/25 06:49:44 maxv Exp $");
__KERNEL_RCSID(0, "$NetBSD: can_pcb.c,v 1.8 2019/07/20 15:34:41 bouyer Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -193,8 +193,8 @@ can_pcbdetach(void *v)
so->so_pcb = NULL;
mutex_enter(&canp->canp_mtx);
can_pcbstate(canp, CANP_DETACHED);
can_pcbsetfilter(canp, NULL, 0);
mutex_exit(&canp->canp_mtx);
can_pcbsetfilter(canp, NULL, 0);
TAILQ_REMOVE(&canp->canp_table->canpt_queue, canp, canp_queue);
sofree(so); /* sofree drops the softnet_lock */
canp_unref(canp);
@ -243,7 +243,9 @@ can_pcbsetfilter(struct canpcb *canp, struct can_filter *fp, int nfilters)
{
struct can_filter *newf;
KASSERT(mutex_owned(&canp->canp_mtx));
struct can_filter *oldf;
int oldnf;
int error = 0;
if (nfilters > 0) {
newf =
@ -252,13 +254,24 @@ can_pcbsetfilter(struct canpcb *canp, struct can_filter *fp, int nfilters)
} else {
newf = NULL;
}
if (canp->canp_filters != NULL) {
kmem_free(canp->canp_filters,
sizeof(struct can_filter) * canp->canp_nfilters);
mutex_enter(&canp->canp_mtx);
oldf = canp->canp_filters;
oldnf = canp->canp_nfilters;
if (newf != NULL && canp->canp_state == CANP_DETACHED) {
error = ECONNRESET;
} else {
canp->canp_filters = newf;
canp->canp_nfilters = nfilters;
newf = NULL;
}
canp->canp_filters = newf;
canp->canp_nfilters = nfilters;
return 0;
mutex_exit(&canp->canp_mtx);
if (oldf != NULL) {
kmem_free(oldf, sizeof(struct can_filter) * oldnf);
}
if (newf != NULL) {
kmem_free(newf, sizeof(struct can_filter) * nfilters);
}
return error;
}