svn commit: r270982 - projects/ipfw/sys/netpfil/ipfw

Alexander V. Chernikov melifaro at FreeBSD.org
Tue Sep 2 20:46:19 UTC 2014


Author: melifaro
Date: Tue Sep  2 20:46:18 2014
New Revision: 270982
URL: http://svnweb.freebsd.org/changeset/base/270982

Log:
  * Fix crash due to forgotten value refcouting in ipfw_link_table_values()
  * Fix argument order in rollback_toperation_state()
  * Make flush_table() use operation state API to ease checks.

Modified:
  projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c
  projects/ipfw/sys/netpfil/ipfw/ip_fw_table_value.c

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c	Tue Sep  2 19:57:33 2014	(r270981)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c	Tue Sep  2 20:46:18 2014	(r270982)
@@ -140,7 +140,7 @@ rollback_toperation_state(struct ip_fw_c
 
 	tcfg = CHAIN_TO_TCFG(ch);
 	TAILQ_FOREACH(os, &tcfg->state_list, next)
-		os->func(os, object);
+		os->func(object, os);
 }
 
 void
@@ -576,6 +576,8 @@ restart:
 		return (error);
 	}
 	ta = tc->ta;
+
+	/* Fill in tablestate */
 	ts.ch = ch;
 	ts.opstate.func = rollback_add_entry;
 	ts.tc = tc;
@@ -1185,12 +1187,26 @@ ipfw_flush_table(struct ip_fw_chain *ch,
 	return (error);
 }
 
+static void
+restart_flush(void *object, struct op_state *_state)
+{
+	struct tableop_state *ts;
+
+	ts = (struct tableop_state *)_state;
+
+	if (ts->tc != object)
+		return;
+
+	/* Indicate we've called */
+	ts->modified = 1;
+}
+
 /*
  * Flushes given table.
  *
  * Function create new table instance with the same
  * parameters, swaps it with old one and
- * flushes state without holding any locks.
+ * flushes state without holding runtime WLOCK.
  *
  * Returns 0 on success.
  */
@@ -1203,6 +1219,7 @@ flush_table(struct ip_fw_chain *ch, stru
 	struct table_info ti_old, ti_new, *tablestate;
 	void *astate_old, *astate_new;
 	char algostate[64], *pstate;
+	struct tableop_state ts;
 	int error;
 	uint16_t kidx;
 	uint8_t tflags;
@@ -1217,13 +1234,18 @@ flush_table(struct ip_fw_chain *ch, stru
 		IPFW_UH_WUNLOCK(ch);
 		return (ESRCH);
 	}
+restart:
+	/* Set up swap handler */
+	memset(&ts, 0, sizeof(ts));
+	ts.opstate.func = restart_flush;
+	ts.tc = tc;
+
 	ta = tc->ta;
 	/* Do not flush readonly tables */
 	if ((ta->flags & TA_FLAG_READONLY) != 0) {
 		IPFW_UH_WUNLOCK(ch);
 		return (EACCES);
 	}
-	tc->no.refcnt++;
 	/* Save startup algo parameters */
 	if (ta->print_config != NULL) {
 		ta->print_config(tc->astate, KIDX_TO_TI(ch, tc->no.kidx),
@@ -1232,24 +1254,39 @@ flush_table(struct ip_fw_chain *ch, stru
 	} else
 		pstate = NULL;
 	tflags = tc->tflags;
+	tc->no.refcnt++;
+	add_toperation_state(ch, &ts);
 	IPFW_UH_WUNLOCK(ch);
 
 	/*
 	 * Stage 2: allocate new table instance using same algo.
 	 */
 	memset(&ti_new, 0, sizeof(struct table_info));
-	if ((error = ta->init(ch, &astate_new, &ti_new, pstate, tflags)) != 0) {
-		IPFW_UH_WLOCK(ch);
-		tc->no.refcnt--;
-		IPFW_UH_WUNLOCK(ch);
-		return (error);
-	}
+	error = ta->init(ch, &astate_new, &ti_new, pstate, tflags);
 
 	/*
 	 * Stage 3: swap old state pointers with newly-allocated ones.
 	 * Decrease refcount.
 	 */
 	IPFW_UH_WLOCK(ch);
+	tc->no.refcnt--;
+	del_toperation_state(ch, &ts);
+
+	if (error != 0) {
+		IPFW_UH_WUNLOCK(ch);
+		return (error);
+	}
+
+	/*
+	 * Restart operation if table swap has happened:
+	 * even if algo may be the same, algo init parameters
+	 * may change. Restart operation instead of doing
+	 * complex checks.
+	 */
+	if (ts.modified != 0) {
+		ta->destroy(astate_new, &ti_new);
+		goto restart;
+	}
 
 	ni = CHAIN_TO_NI(ch);
 	kidx = tc->no.kidx;
@@ -1264,7 +1301,6 @@ flush_table(struct ip_fw_chain *ch, stru
 	tc->astate = astate_new;
 	tc->ti = ti_new;
 	tc->count = 0;
-	tc->no.refcnt--;
 
 	/*
 	 * Stage 4: unref values.

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_table_value.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_table_value.c	Tue Sep  2 19:57:33 2014	(r270981)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_table_value.c	Tue Sep  2 20:46:18 2014	(r270982)
@@ -531,8 +531,8 @@ ipfw_link_table_values(struct ip_fw_chai
 
 		/* Value found. Bump refcount */
 		ptv->pval->refcnt++;
-		found++;
 		ptei->value = ptv->no.kidx;
+		found++;
 	}
 
 	if (ts->count == found) {
@@ -585,6 +585,7 @@ ipfw_link_table_values(struct ip_fw_chai
 		ptv = (struct table_val_link *)ipfw_objhash_lookup_name(vi, 0,
 		    (char *)&tval);
 		if (ptv != NULL) {
+			ptv->pval->refcnt++;
 			ptei->value = ptv->no.kidx;
 			continue;
 		}


More information about the svn-src-projects mailing list