svn commit: r216353 - stable/8/sys/kern

Konstantin Belousov kib at FreeBSD.org
Fri Dec 10 10:48:55 UTC 2010


Author: kib
Date: Fri Dec 10 10:48:54 2010
New Revision: 216353
URL: http://svn.freebsd.org/changeset/base/216353

Log:
  MFC r216150, r216158:
  If unix socket has a unix socket attached as the rights that has a
  unix socket attached as the rights that has a unix socket attached as
  the rights ... Kernel may overflow the stack on attempt to close such
  socket.
  
  Only close the rights file in the context of the current close if the
  file is not unix domain socket. Otherwise, postpone the work to
  taskqueue, preventing unlimited recursion.
  
  Approved by:	re (bz)

Modified:
  stable/8/sys/kern/uipc_usrreq.c
Directory Properties:
  stable/8/sys/   (props changed)
  stable/8/sys/amd64/include/xen/   (props changed)
  stable/8/sys/cddl/contrib/opensolaris/   (props changed)
  stable/8/sys/contrib/dev/acpica/   (props changed)
  stable/8/sys/contrib/pf/   (props changed)

Modified: stable/8/sys/kern/uipc_usrreq.c
==============================================================================
--- stable/8/sys/kern/uipc_usrreq.c	Fri Dec 10 10:37:53 2010	(r216352)
+++ stable/8/sys/kern/uipc_usrreq.c	Fri Dec 10 10:48:54 2010	(r216353)
@@ -75,6 +75,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/namei.h>
 #include <sys/proc.h>
 #include <sys/protosw.h>
+#include <sys/queue.h>
 #include <sys/resourcevar.h>
 #include <sys/rwlock.h>
 #include <sys/socket.h>
@@ -113,6 +114,13 @@ static int		unp_rights;	/* (g) File desc
 static struct unp_head	unp_shead;	/* (l) List of stream sockets. */
 static struct unp_head	unp_dhead;	/* (l) List of datagram sockets. */
 
+struct unp_defer {
+	SLIST_ENTRY(unp_defer) ud_link;
+	struct file *ud_fp;
+};
+static SLIST_HEAD(, unp_defer) unp_defers;
+static int unp_defers_count;
+
 static const struct sockaddr	sun_noname = { sizeof(sun_noname), AF_LOCAL };
 
 /*
@@ -124,6 +132,13 @@ static const struct sockaddr	sun_noname 
 static struct task	unp_gc_task;
 
 /*
+ * The close of unix domain sockets attached as SCM_RIGHTS is
+ * postponed to the taskqueue, to avoid arbitrary recursion depth.
+ * The attached sockets might have another sockets attached.
+ */
+static struct task	unp_defer_task;
+
+/*
  * Both send and receive buffers are allocated PIPSIZ bytes of buffering for
  * stream sockets, although the total for sender and receiver is actually
  * only PIPSIZ.
@@ -152,8 +167,11 @@ SYSCTL_ULONG(_net_local_dgram, OID_AUTO,
 	   &unpdg_sendspace, 0, "Default datagram send space.");
 SYSCTL_ULONG(_net_local_dgram, OID_AUTO, recvspace, CTLFLAG_RW,
 	   &unpdg_recvspace, 0, "Default datagram receive space.");
-SYSCTL_INT(_net_local, OID_AUTO, inflight, CTLFLAG_RD, &unp_rights, 0, 
+SYSCTL_INT(_net_local, OID_AUTO, inflight, CTLFLAG_RD, &unp_rights, 0,
     "File descriptors in flight.");
+SYSCTL_INT(_net_local, OID_AUTO, deferred, CTLFLAG_RD,
+    &unp_defers_count, 0,
+    "File descriptors deferred to taskqueue for close.");
 
 /*-
  * Locking and synchronization:
@@ -203,6 +221,7 @@ SYSCTL_INT(_net_local, OID_AUTO, infligh
  */
 static struct rwlock	unp_link_rwlock;
 static struct mtx	unp_list_lock;
+static struct mtx	unp_defers_lock;
 
 #define	UNP_LINK_LOCK_INIT()		rw_init(&unp_link_rwlock,	\
 					    "unp_link_rwlock")
@@ -224,6 +243,11 @@ static struct mtx	unp_list_lock;
 #define	UNP_LIST_LOCK()			mtx_lock(&unp_list_lock)
 #define	UNP_LIST_UNLOCK()		mtx_unlock(&unp_list_lock)
 
+#define	UNP_DEFERRED_LOCK_INIT()	mtx_init(&unp_defers_lock, \
+					    "unp_defer", NULL, MTX_DEF)
+#define	UNP_DEFERRED_LOCK()		mtx_lock(&unp_defers_lock)
+#define	UNP_DEFERRED_UNLOCK()		mtx_unlock(&unp_defers_lock)
+
 #define UNP_PCB_LOCK_INIT(unp)		mtx_init(&(unp)->unp_mtx,	\
 					    "unp_mtx", "unp_mtx",	\
 					    MTX_DUPOK|MTX_DEF|MTX_RECURSE)
@@ -249,8 +273,9 @@ static void	unp_init(void);
 static int	unp_internalize(struct mbuf **, struct thread *);
 static void	unp_internalize_fp(struct file *);
 static int	unp_externalize(struct mbuf *, struct mbuf **);
-static void	unp_externalize_fp(struct file *);
+static int	unp_externalize_fp(struct file *);
 static struct mbuf	*unp_addsockcred(struct thread *, struct mbuf *);
+static void	unp_process_defers(void * __unused, int);
 
 /*
  * Definitions of protocols supported in the LOCAL domain.
@@ -1658,9 +1683,12 @@ unp_init(void)
 	    NULL, EVENTHANDLER_PRI_ANY);
 	LIST_INIT(&unp_dhead);
 	LIST_INIT(&unp_shead);
+	SLIST_INIT(&unp_defers);
 	TASK_INIT(&unp_gc_task, 0, unp_gc, NULL);
+	TASK_INIT(&unp_defer_task, 0, unp_process_defers, NULL);
 	UNP_LINK_LOCK_INIT();
 	UNP_LIST_LOCK_INIT();
+	UNP_DEFERRED_LOCK_INIT();
 }
 
 static int
@@ -1864,9 +1892,45 @@ fptounp(struct file *fp)
 static void
 unp_discard(struct file *fp)
 {
+	struct unp_defer *dr;
+
+	if (unp_externalize_fp(fp)) {
+		dr = malloc(sizeof(*dr), M_TEMP, M_WAITOK);
+		dr->ud_fp = fp;
+		UNP_DEFERRED_LOCK();
+		SLIST_INSERT_HEAD(&unp_defers, dr, ud_link);
+		UNP_DEFERRED_UNLOCK();
+		atomic_add_int(&unp_defers_count, 1);
+		taskqueue_enqueue(taskqueue_thread, &unp_defer_task);
+	} else
+		(void) closef(fp, (struct thread *)NULL);
+}
+
+static void
+unp_process_defers(void *arg __unused, int pending)
+{
+	struct unp_defer *dr;
+	SLIST_HEAD(, unp_defer) drl;
+	int count;
 
-	unp_externalize_fp(fp);
-	(void) closef(fp, (struct thread *)NULL);
+	SLIST_INIT(&drl);
+	for (;;) {
+		UNP_DEFERRED_LOCK();
+		if (SLIST_FIRST(&unp_defers) == NULL) {
+			UNP_DEFERRED_UNLOCK();
+			break;
+		}
+		SLIST_SWAP(&unp_defers, &drl, unp_defer);
+		UNP_DEFERRED_UNLOCK();
+		count = 0;
+		while ((dr = SLIST_FIRST(&drl)) != NULL) {
+			SLIST_REMOVE_HEAD(&drl, ud_link);
+			closef(dr->ud_fp, NULL);
+			free(dr, M_TEMP);
+			count++;
+		}
+		atomic_add_int(&unp_defers_count, -count);
+	}
 }
 
 static void
@@ -1884,16 +1948,21 @@ unp_internalize_fp(struct file *fp)
 	UNP_LINK_WUNLOCK();
 }
 
-static void
+static int
 unp_externalize_fp(struct file *fp)
 {
 	struct unpcb *unp;
+	int ret;
 
 	UNP_LINK_WLOCK();
-	if ((unp = fptounp(fp)) != NULL)
+	if ((unp = fptounp(fp)) != NULL) {
 		unp->unp_msgcount--;
+		ret = 1;
+	} else
+		ret = 0;
 	unp_rights--;
 	UNP_LINK_WUNLOCK();
+	return (ret);
 }
 
 /*


More information about the svn-src-all mailing list