xfrm: do not assume that template resolving always returns xfrms
authorTimo Teräs <timo.teras@iki.fi>
Mon, 12 Jul 2010 21:29:42 +0000 (21:29 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 14 Jul 2010 21:16:48 +0000 (14:16 -0700)
xfrm_resolve_and_create_bundle() assumed that, if policies indicated
presence of xfrms, bundle template resolution would always return
some xfrms. This is not true for 'use' level policies which can
result in no xfrm's being applied if there is no suitable xfrm states.
This fixes a crash by this incorrect assumption.

Reported-by: George Spelvin <linux@horizon.com>
Bisected-by: George Spelvin <linux@horizon.com>
Tested-by: George Spelvin <linux@horizon.com>
Signed-off-by: Timo Teräs <timo.teras@iki.fi>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/xfrm/xfrm_policy.c

index af1c173be4ad88906d55d7efa24fb47fcd0cf19d..a7ec5a8a2380e6277fa4a3755e01fb0aa58076fb 100644 (file)
@@ -1594,8 +1594,8 @@ xfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols,
 
        /* Try to instantiate a bundle */
        err = xfrm_tmpl_resolve(pols, num_pols, fl, xfrm, family);
-       if (err < 0) {
-               if (err != -EAGAIN)
+       if (err <= 0) {
+               if (err != 0 && err != -EAGAIN)
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLERROR);
                return ERR_PTR(err);
        }
@@ -1678,6 +1678,13 @@ xfrm_bundle_lookup(struct net *net, struct flowi *fl, u16 family, u8 dir,
                        goto make_dummy_bundle;
                dst_hold(&xdst->u.dst);
                return oldflo;
+       } else if (new_xdst == NULL) {
+               num_xfrms = 0;
+               if (oldflo == NULL)
+                       goto make_dummy_bundle;
+               xdst->num_xfrms = 0;
+               dst_hold(&xdst->u.dst);
+               return oldflo;
        }
 
        /* Kill the previous bundle */
@@ -1760,6 +1767,10 @@ restart:
                                xfrm_pols_put(pols, num_pols);
                                err = PTR_ERR(xdst);
                                goto dropdst;
+                       } else if (xdst == NULL) {
+                               num_xfrms = 0;
+                               drop_pols = num_pols;
+                               goto no_transform;
                        }
 
                        spin_lock_bh(&xfrm_policy_sk_bundle_lock);