datapath: Don't write into IPV4_TUNNEL data when using TUN_ID.
authorJesse Gross <jesse@nicira.com>
Mon, 5 Nov 2012 19:30:35 +0000 (11:30 -0800)
committerJesse Gross <jesse@nicira.com>
Wed, 7 Nov 2012 07:27:54 +0000 (23:27 -0800)
When the IPV4_TUNNEL action is executed, a pointer in the skb is
directly assigned the address of the action, which is protected by
RCU.  If a TUN_ID action is later executed it will write into the
action, which is not allowed by RCU.  This avoids the problem by
making a copy of the data and writing into the copy.

Signed-off-by: Jesse Gross <jesse@nicira.com>
Acked-by: Kyle Mestery <kmestery@cisco.com>
datapath/actions.c

index 4649c050c38a68ff7c14820e5eb05c9b56d3fbaa..8ec692d1d3d61c03aba0628393eff1ce8acc7efa 100644 (file)
@@ -346,15 +346,17 @@ static int execute_set_action(struct sk_buff *skb,
                break;
 
        case OVS_KEY_ATTR_TUN_ID:
-               if (!OVS_CB(skb)->tun_key) {
-                       /* If tun_key is NULL for this skb, assign it to
-                        * a value the caller passed in for action processing
-                        * and output. This can disappear once we drop support
-                        * for setting tun_id outside of tun_key.
-                        */
-                       memset(tun_key, 0, sizeof(struct ovs_key_ipv4_tunnel));
-                       OVS_CB(skb)->tun_key = tun_key;
-               }
+               /* If we're only using the TUN_ID action, store the value in a
+                * temporary instance of struct ovs_key_ipv4_tunnel on the stack.
+                * If both IPV4_TUNNEL and TUN_ID are being used together we
+                * can't write into the IPV4_TUNNEL action, so make a copy and
+                * write into that version.
+                */
+               if (!OVS_CB(skb)->tun_key)
+                       memset(tun_key, 0, sizeof(*tun_key));
+               else if (OVS_CB(skb)->tun_key != tun_key)
+                       memcpy(tun_key, OVS_CB(skb)->tun_key, sizeof(*tun_key));
+               OVS_CB(skb)->tun_key = tun_key;
 
                OVS_CB(skb)->tun_key->tun_id = nla_get_be64(nested_attr);
                break;