SELinux: Fix a RCU free problem with the netport cache

The netport cache doesn't free resources in a manner which is safe or orderly.
This patch fixes this by adding in a missing call to rcu_dereference() in
sel_netport_insert() as well as some general cleanup throughout the file.

Signed-off-by: Paul Moore <paul.moore@hp.com>
Signed-off-by: James Morris <jmorris@namei.org>
diff --git a/security/selinux/netport.c b/security/selinux/netport.c
index 68ede3c..90b4cff 100644
--- a/security/selinux/netport.c
+++ b/security/selinux/netport.c
@@ -114,8 +114,7 @@
 
 	idx = sel_netport_hashfn(pnum);
 	list_for_each_entry_rcu(port, &sel_netport_hash[idx].list, list)
-		if (port->psec.port == pnum &&
-		    port->psec.protocol == protocol)
+		if (port->psec.port == pnum && port->psec.protocol == protocol)
 			return port;
 
 	return NULL;
@@ -126,11 +125,10 @@
  * @port: the new port record
  *
  * Description:
- * Add a new port record to the network address hash table.  Returns zero on
- * success, negative values on failure.
+ * Add a new port record to the network address hash table.
  *
  */
-static int sel_netport_insert(struct sel_netport *port)
+static void sel_netport_insert(struct sel_netport *port)
 {
 	unsigned int idx;
 
@@ -140,13 +138,13 @@
 	list_add_rcu(&port->list, &sel_netport_hash[idx].list);
 	if (sel_netport_hash[idx].size == SEL_NETPORT_HASH_BKT_LIMIT) {
 		struct sel_netport *tail;
-		tail = list_entry(port->list.prev, struct sel_netport, list);
-		list_del_rcu(port->list.prev);
+		tail = list_entry(
+			rcu_dereference(sel_netport_hash[idx].list.prev),
+			struct sel_netport, list);
+		list_del_rcu(&tail->list);
 		call_rcu(&tail->rcu, sel_netport_free);
 	} else
 		sel_netport_hash[idx].size++;
-
-	return 0;
 }
 
 /**
@@ -163,7 +161,7 @@
  */
 static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid)
 {
-	int ret;
+	int ret = -ENOMEM;
 	struct sel_netport *port;
 	struct sel_netport *new = NULL;
 
@@ -171,23 +169,20 @@
 	port = sel_netport_find(protocol, pnum);
 	if (port != NULL) {
 		*sid = port->psec.sid;
-		ret = 0;
-		goto out;
+		spin_unlock_bh(&sel_netport_lock);
+		return 0;
 	}
 	new = kzalloc(sizeof(*new), GFP_ATOMIC);
-	if (new == NULL) {
-		ret = -ENOMEM;
+	if (new == NULL)
 		goto out;
-	}
-	ret = security_port_sid(protocol, pnum, &new->psec.sid);
+	ret = security_port_sid(protocol, pnum, sid);
 	if (ret != 0)
 		goto out;
+
 	new->psec.port = pnum;
 	new->psec.protocol = protocol;
-	ret = sel_netport_insert(new);
-	if (ret != 0)
-		goto out;
-	*sid = new->psec.sid;
+	new->psec.sid = *sid;
+	sel_netport_insert(new);
 
 out:
 	spin_unlock_bh(&sel_netport_lock);
@@ -239,11 +234,12 @@
 static void sel_netport_flush(void)
 {
 	unsigned int idx;
-	struct sel_netport *port;
+	struct sel_netport *port, *port_tmp;
 
 	spin_lock_bh(&sel_netport_lock);
 	for (idx = 0; idx < SEL_NETPORT_HASH_SIZE; idx++) {
-		list_for_each_entry(port, &sel_netport_hash[idx].list, list) {
+		list_for_each_entry_safe(port, port_tmp,
+					 &sel_netport_hash[idx].list, list) {
 			list_del_rcu(&port->list);
 			call_rcu(&port->rcu, sel_netport_free);
 		}