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:
parent
4b86db61e8
commit
8129437f91
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue