Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/netipsec Fix kernel panic (assertion failure) on receivi...



details:   https://anonhg.NetBSD.org/src/rev/a8619c4592f5
branches:  trunk
changeset: 359551:a8619c4592f5
user:      ozaki-r <ozaki-r%NetBSD.org@localhost>
date:      Thu Feb 15 04:27:24 2018 +0000

description:
Fix kernel panic (assertion failure) on receiving an IPv6 packet with large options

If an IPv6 packet has large options, a necessary space for evacuation can
exceed the expected size (ah_pool_item_size). Give up using the pool_cache
if it happens.

Pointed out by maxv@

diffstat:

 sys/netipsec/xform_ah.c |  54 ++++++++++++++++++++++++++++++++++++------------
 1 files changed, 40 insertions(+), 14 deletions(-)

diffs (131 lines):

diff -r 18f4b621673e -r a8619c4592f5 sys/netipsec/xform_ah.c
--- a/sys/netipsec/xform_ah.c   Thu Feb 15 04:24:32 2018 +0000
+++ b/sys/netipsec/xform_ah.c   Thu Feb 15 04:27:24 2018 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: xform_ah.c,v 1.78 2018/02/15 04:24:32 ozaki-r Exp $    */
+/*     $NetBSD: xform_ah.c,v 1.79 2018/02/15 04:27:24 ozaki-r Exp $    */
 /*     $FreeBSD: src/sys/netipsec/xform_ah.c,v 1.1.4.1 2003/01/24 05:11:36 sam Exp $   */
 /*     $OpenBSD: ip_ah.c,v 1.63 2001/06/26 06:18:58 angelos Exp $ */
 /*
@@ -39,7 +39,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: xform_ah.c,v 1.78 2018/02/15 04:24:32 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: xform_ah.c,v 1.79 2018/02/15 04:27:24 ozaki-r Exp $");
 
 #if defined(_KERNEL_OPT)
 #include "opt_inet.h"
@@ -55,6 +55,7 @@
 #include <sys/sysctl.h>
 #include <sys/pool.h>
 #include <sys/pserialize.h>
+#include <sys/kmem.h>
 
 #include <net/if.h>
 
@@ -621,6 +622,7 @@
        int hl, rplen, authsize, error, stat = AH_STAT_HDROPS;
        struct cryptodesc *crda;
        struct cryptop *crp = NULL;
+       bool pool_used;
 
        IPSEC_SPLASSERT_SOFTNET(__func__);
 
@@ -693,9 +695,14 @@
        size_t extra = skip + rplen + authsize;
        size += extra;
 
-       KASSERTMSG(size <= ah_pool_item_size,
-           "size=%zu > ah_pool_item_size=%zu\n", size, ah_pool_item_size);
-       tc = pool_cache_get(ah_tdb_crypto_pool_cache, PR_NOWAIT);
+       if (__predict_true(size <= ah_pool_item_size)) {
+               tc = pool_cache_get(ah_tdb_crypto_pool_cache, PR_NOWAIT);
+               pool_used = true;
+       } else {
+               /* size can exceed on IPv6 packets with large options.  */
+               tc = kmem_intr_zalloc(size, KM_NOSLEEP);
+               pool_used = false;
+       }
        if (tc == NULL) {
                DPRINTF(("%s: failed to allocate tdb_crypto\n", __func__));
                stat = AH_STAT_CRYPTO;
@@ -767,8 +774,12 @@
        return crypto_dispatch(crp);
 
 bad:
-       if (tc != NULL)
-               pool_cache_put(ah_tdb_crypto_pool_cache, tc);
+       if (tc != NULL) {
+               if (__predict_true(pool_used))
+                       pool_cache_put(ah_tdb_crypto_pool_cache, tc);
+               else
+                       kmem_intr_free(tc, size);
+       }
        if (crp != NULL)
                crypto_freereq(crp);
        if (m != NULL)
@@ -808,6 +819,8 @@
        int authsize;
        uint16_t dport;
        uint16_t sport;
+       bool pool_used;
+       size_t size;
        IPSEC_DECLARE_LOCK_VARIABLE;
 
        KASSERT(crp->crp_opaque != NULL);
@@ -829,6 +842,16 @@
            saidx->dst.sa.sa_family == AF_INET6,
            "unexpected protocol family %u", saidx->dst.sa.sa_family);
 
+       /* Figure out header size. */
+       rplen = HDRSIZE(sav);
+       authsize = AUTHSIZE(sav);
+
+       size = sizeof(*tc) + skip + rplen + authsize;
+       if (__predict_true(size <= ah_pool_item_size))
+               pool_used = true;
+       else
+               pool_used = false;
+
        /* Check for crypto errors. */
        if (crp->crp_etype) {
                if (sav->tdb_cryptoid != 0)
@@ -849,10 +872,6 @@
                crp = NULL;
        }
 
-       /* Figure out header size. */
-       rplen = HDRSIZE(sav);
-       authsize = AUTHSIZE(sav);
-
        if (ipsec_debug)
                memset(calc, 0, sizeof(calc));
 
@@ -890,7 +909,10 @@
        /* Copyback the saved (uncooked) network headers. */
        m_copyback(m, 0, skip, ptr);
 
-       pool_cache_put(ah_tdb_crypto_pool_cache, tc);
+       if (__predict_true(pool_used))
+               pool_cache_put(ah_tdb_crypto_pool_cache, tc);
+       else
+               kmem_intr_free(tc, size);
        tc = NULL;
 
        /*
@@ -937,8 +959,12 @@
        IPSEC_RELEASE_GLOBAL_LOCKS();
        if (m != NULL)
                m_freem(m);
-       if (tc != NULL)
-               pool_cache_put(ah_tdb_crypto_pool_cache, tc);
+       if (tc != NULL) {
+               if (pool_used)
+                       pool_cache_put(ah_tdb_crypto_pool_cache, tc);
+               else
+                       kmem_intr_free(tc, size);
+       }
        if (crp != NULL)
                crypto_freereq(crp);
        return error;



Home | Main Index | Thread Index | Old Index