On Thu, Apr 19, 2018 at 04:47:36PM -0400, mabi wrote:
> The VPN works fine for low data traffic but as soon as I start a big transfer 
> between the two sites the kernel panics when iked wants to rekey the SA. I 
> can reproduce this on demand by using the iperf tool for example. 

Please test the following patch. It ensures that a session is not freed
while it is in use.


Index: arch/amd64/amd64/aesni.c
===================================================================
RCS file: src/sys/arch/amd64/amd64/aesni.c,v
retrieving revision 1.44
diff -u -p -r1.44 aesni.c
--- arch/amd64/amd64/aesni.c    9 Apr 2018 04:34:56 -0000       1.44
+++ arch/amd64/amd64/aesni.c    20 Apr 2018 01:43:02 -0000
@@ -21,6 +21,7 @@
 
 #include <sys/param.h>
 #include <sys/systm.h>
+#include <sys/atomic.h>
 #include <sys/queue.h>
 #include <sys/malloc.h>
 #include <sys/pool.h>
@@ -50,6 +51,7 @@ struct aesni_session {
        uint32_t                 ses_dkey[4 * (AES_MAXROUNDS + 1)];
        uint32_t                 ses_klen;
        uint8_t                  ses_nonce[AESCTR_NONCESIZE];
+       unsigned int             ses_refs;
        int                      ses_sid;
        GHASH_CTX               *ses_ghash;
        struct aesni_xts_ctx    *ses_xts;
@@ -105,6 +107,10 @@ int        aesni_newsession(u_int32_t *, struct
 int    aesni_freesession(u_int64_t);
 int    aesni_process(struct cryptop *);
 
+struct aesni_session *
+       aesni_get(uint32_t);
+void   aesni_put(struct aesni_session *);
+
 int    aesni_swauth(struct cryptop *, struct cryptodesc *, struct swcr_data *,
            caddr_t);
 
@@ -181,15 +187,11 @@ aesni_newsession(u_int32_t *sidp, struct
        if (!ses)
                return (ENOMEM);
 
+       ses->ses_refs = 1;
        ses->ses_buf = malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT|M_ZERO);
        if (ses->ses_buf != NULL)
                ses->ses_buflen = PAGE_SIZE;
 
-       mtx_enter(&aesni_sc->sc_mtx);
-       LIST_INSERT_HEAD(&aesni_sc->sc_sessions, ses, ses_entries);
-       mtx_leave(&aesni_sc->sc_mtx);
-       ses->ses_sid = ++aesni_sc->sc_sid;
-
        for (c = cri; c != NULL; c = c->cri_next) {
                switch (c->cri_alg) {
                case CRYPTO_AES_CBC:
@@ -214,7 +216,7 @@ aesni_newsession(u_int32_t *sidp, struct
                        ses->ses_xts = malloc(sizeof(struct aesni_xts_ctx),
                            M_CRYPTO_DATA, M_NOWAIT | M_ZERO);
                        if (ses->ses_xts == NULL) {
-                               aesni_freesession(ses->ses_sid);
+                               aesni_put(ses);
                                return (ENOMEM);
                        }
 
@@ -238,7 +240,7 @@ aesni_newsession(u_int32_t *sidp, struct
                        ses->ses_ghash = malloc(sizeof(GHASH_CTX),
                            M_CRYPTO_DATA, M_NOWAIT | M_ZERO);
                        if (ses->ses_ghash == NULL) {
-                               aesni_freesession(ses->ses_sid);
+                               aesni_put(ses);
                                return (ENOMEM);
                        }
 
@@ -267,7 +269,7 @@ aesni_newsession(u_int32_t *sidp, struct
                        swd = malloc(sizeof(struct swcr_data), M_CRYPTO_DATA,
                            M_NOWAIT|M_ZERO);
                        if (swd == NULL) {
-                               aesni_freesession(ses->ses_sid);
+                               aesni_put(ses);
                                return (ENOMEM);
                        }
                        ses->ses_swd = swd;
@@ -275,14 +277,14 @@ aesni_newsession(u_int32_t *sidp, struct
                        swd->sw_ictx = malloc(axf->ctxsize, M_CRYPTO_DATA,
                            M_NOWAIT);
                        if (swd->sw_ictx == NULL) {
-                               aesni_freesession(ses->ses_sid);
+                               aesni_put(ses);
                                return (ENOMEM);
                        }
 
                        swd->sw_octx = malloc(axf->ctxsize, M_CRYPTO_DATA,
                            M_NOWAIT);
                        if (swd->sw_octx == NULL) {
-                               aesni_freesession(ses->ses_sid);
+                               aesni_put(ses);
                                return (ENOMEM);
                        }
 
@@ -316,11 +318,16 @@ aesni_newsession(u_int32_t *sidp, struct
                        break;
 
                default:
-                       aesni_freesession(ses->ses_sid);
+                       aesni_put(ses);
                        return (EINVAL);
                }
        }
 
+       mtx_enter(&aesni_sc->sc_mtx);
+       LIST_INSERT_HEAD(&aesni_sc->sc_sessions, ses, ses_entries);
+       ses->ses_sid = ++aesni_sc->sc_sid;
+       mtx_leave(&aesni_sc->sc_mtx);
+
        *sidp = ses->ses_sid;
        return (0);
 }
@@ -329,23 +336,33 @@ int
 aesni_freesession(u_int64_t tid)
 {
        struct aesni_session *ses;
-       struct swcr_data *swd;
-       struct auth_hash *axf;
        u_int32_t sid = (u_int32_t)tid;
 
        mtx_enter(&aesni_sc->sc_mtx);
        LIST_FOREACH(ses, &aesni_sc->sc_sessions, ses_entries) {
-               if (ses->ses_sid == sid)
+               if (ses->ses_sid == sid) {
+                       LIST_REMOVE(ses, ses_entries);
                        break;
+               }
        }
        mtx_leave(&aesni_sc->sc_mtx);
 
        if (ses == NULL)
                return (EINVAL);
 
-       mtx_enter(&aesni_sc->sc_mtx);
-       LIST_REMOVE(ses, ses_entries);
-       mtx_leave(&aesni_sc->sc_mtx);
+       aesni_put(ses);
+
+       return (0);
+}
+
+void
+aesni_put(struct aesni_session *ses)
+{
+       struct swcr_data *swd;
+       struct auth_hash *axf;
+
+       if (atomic_dec_int_nv(&ses->ses_refs) > 0)
+               return;
 
        if (ses->ses_ghash) {
                explicit_bzero(ses->ses_ghash, sizeof(GHASH_CTX));
@@ -379,8 +396,22 @@ aesni_freesession(u_int64_t tid)
 
        explicit_bzero(ses, sizeof (*ses));
        pool_put(&aesnipl, ses);
+}
 
-       return (0);
+struct aesni_session *
+aesni_get(uint32_t sid)
+{
+       struct aesni_session *ses = NULL;
+
+       mtx_enter(&aesni_sc->sc_mtx);
+       LIST_FOREACH(ses, &aesni_sc->sc_sessions, ses_entries) {
+               if (ses->ses_sid == sid) {
+                       atomic_inc_int(&ses->ses_refs);
+                       break;
+               }
+       }
+       mtx_leave(&aesni_sc->sc_mtx);
+       return (ses);
 }
 
 int
@@ -609,13 +640,7 @@ aesni_process(struct cryptop *crp)
        if (crp->crp_ndesc < 1)
                return (EINVAL);
 
-       mtx_enter(&aesni_sc->sc_mtx);
-       LIST_FOREACH(ses, &aesni_sc->sc_sessions, ses_entries) {
-               if (ses->ses_sid == (crp->crp_sid & 0xffffffff))
-                       break;
-       }
-       mtx_leave(&aesni_sc->sc_mtx);
-
+       ses = aesni_get(crp->crp_sid & 0xffffffff);
        if (!ses) {
                err = EINVAL;
                goto out;
@@ -670,6 +695,8 @@ aesni_process(struct cryptop *crp)
        }
 
 out:
+       if (ses != NULL)
+               aesni_put(ses);
        crp->crp_etype = err;
        crypto_done(crp);
        return (err);

Reply via email to