Print this page
11556 ip_attr.c functions need to not dereference conn_ixa directly after lock drop
Reviewed by: Jason King <jbk@joyent.com>
Reviewed by: Mike Gerdts <mgerdts@joyent.com>
Reviewed by: Andy Fiddaman <andy@omniosce.org>
@@ -22,10 +22,14 @@
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/* Copyright (c) 1990 Mentat Inc. */
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
#include <sys/types.h>
#include <sys/stream.h>
#include <sys/strsun.h>
#include <sys/zone.h>
#include <sys/ddi.h>
@@ -95,20 +99,23 @@
#include <sys/tsol/tnet.h>
/*
* Release a reference on ip_xmit_attr.
* The reference is acquired by conn_get_ixa()
+ *
+ * This macro has a lowercase function-call version for callers outside
+ * this file.
*/
#define IXA_REFRELE(ixa) \
{ \
if (atomic_dec_32_nv(&(ixa)->ixa_refcnt) == 0) \
ixa_inactive(ixa); \
}
#define IXA_REFHOLD(ixa) \
{ \
- ASSERT((ixa)->ixa_refcnt != 0); \
+ ASSERT3U((ixa)->ixa_refcnt, !=, 0); \
atomic_inc_32(&(ixa)->ixa_refcnt); \
}
/*
* When we need to handle a transmit side asynchronous operation, then we need
@@ -744,40 +751,45 @@
}
static ip_xmit_attr_t *
conn_get_ixa_impl(conn_t *connp, boolean_t replace, int kmflag)
{
- ip_xmit_attr_t *ixa;
- ip_xmit_attr_t *oldixa;
+ ip_xmit_attr_t *oldixa; /* Already attached to conn_t */
+ ip_xmit_attr_t *ixa; /* New one, which we return. */
+ /*
+ * NOTE: If the marked-below common case isn't, move the
+ * kmem_alloc() up here and put a free in what was marked as the
+ * (not really) common case instead.
+ */
+
mutex_enter(&connp->conn_lock);
- ixa = connp->conn_ixa;
+ oldixa = connp->conn_ixa;
- /* At least one references for the conn_t */
- ASSERT(ixa->ixa_refcnt >= 1);
- if (atomic_inc_32_nv(&ixa->ixa_refcnt) == 2) {
- /* No other thread using conn_ixa */
+ /* At least one reference for the conn_t */
+ ASSERT3U(oldixa->ixa_refcnt, >=, 1);
+ if (atomic_inc_32_nv(&oldixa->ixa_refcnt) == 2) {
+ /* No other thread using conn_ixa (common case) */
mutex_exit(&connp->conn_lock);
- return (ixa);
+ return (oldixa);
}
+ /* Do allocation inside-the-conn_lock because it's less common. */
ixa = kmem_alloc(sizeof (*ixa), kmflag);
if (ixa == NULL) {
mutex_exit(&connp->conn_lock);
- ixa_refrele(connp->conn_ixa);
+ IXA_REFRELE(oldixa);
return (NULL);
}
- ixa_safe_copy(connp->conn_ixa, ixa);
+ ixa_safe_copy(oldixa, ixa);
/* Make sure we drop conn_lock before any refrele */
if (replace) {
ixa->ixa_refcnt++; /* No atomic needed - not visible */
- oldixa = connp->conn_ixa;
connp->conn_ixa = ixa;
mutex_exit(&connp->conn_lock);
IXA_REFRELE(oldixa); /* Undo refcnt from conn_t */
} else {
- oldixa = connp->conn_ixa;
mutex_exit(&connp->conn_lock);
}
IXA_REFRELE(oldixa); /* Undo above atomic_add_32_nv */
return (ixa);
@@ -845,30 +857,25 @@
* if ip_set_destination needs to be called to re-establish the pointers.
*/
ip_xmit_attr_t *
conn_get_ixa_exclusive(conn_t *connp)
{
+ ip_xmit_attr_t *oldixa;
ip_xmit_attr_t *ixa;
+ ixa = kmem_alloc(sizeof (*ixa), KM_NOSLEEP | KM_NORMALPRI);
+ if (ixa == NULL)
+ return (NULL);
+
mutex_enter(&connp->conn_lock);
- ixa = connp->conn_ixa;
- /* At least one references for the conn_t */
- ASSERT(ixa->ixa_refcnt >= 1);
+ oldixa = connp->conn_ixa;
+ IXA_REFHOLD(oldixa);
- /* Make sure conn_ixa doesn't disappear while we copy it */
- atomic_inc_32(&ixa->ixa_refcnt);
-
- ixa = kmem_alloc(sizeof (*ixa), KM_NOSLEEP);
- if (ixa == NULL) {
+ ixa_safe_copy(oldixa, ixa);
mutex_exit(&connp->conn_lock);
- ixa_refrele(connp->conn_ixa);
- return (NULL);
- }
- ixa_safe_copy(connp->conn_ixa, ixa);
- mutex_exit(&connp->conn_lock);
- IXA_REFRELE(connp->conn_ixa);
+ IXA_REFRELE(oldixa);
return (ixa);
}
void
ixa_safe_copy(ip_xmit_attr_t *src, ip_xmit_attr_t *ixa)
@@ -1355,11 +1362,11 @@
conn_t *, connp);
return;
}
}
ixa_cleanup_stale(ixa);
- ixa_refrele(ixa);
+ IXA_REFRELE(ixa);
}
}
/*
* ixa needs to be an exclusive copy so that no one changes the cookie