summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormarius <marius@FreeBSD.org>2017-09-21 19:30:32 +0000
committermarius <marius@FreeBSD.org>2017-09-21 19:30:32 +0000
commitc5d73cb5c385612d2cc3b239336ccda5b8b2c4ef (patch)
treea5da3827dca981fa872c434dcadde16cc22f4469
parentee65f3381759668f6c2764e955c7d73070eb732e (diff)
downloadFreeBSD-src-c5d73cb5c385612d2cc3b239336ccda5b8b2c4ef.zip
FreeBSD-src-c5d73cb5c385612d2cc3b239336ccda5b8b2c4ef.tar.gz
MFC: r285215
remove _NORMAL flag which isn't suppose to be used w/ _alloc_ctx... MFC: r285289 address an issue where consumers, like IPsec, can reuse the same session in multiple threads w/o locking.. There was a single fpu context shared per session, if multiple threads were using the session, and both migrated away, they could corrupt each other's fpu context... MFC: r285297 upon further examination, it turns out that _unregister_all already provides the guarantee that no threads will be in the _newsession code.. MFC: r298332 aesni(4): Initialize error before use [1] Reported by: Coverity [1] CID: 1331554 [1]
-rw-r--r--sys/crypto/aesni/aesni.c127
-rw-r--r--sys/crypto/aesni/aesni.h1
2 files changed, 100 insertions, 28 deletions
diff --git a/sys/crypto/aesni/aesni.c b/sys/crypto/aesni/aesni.c
index 7d7a740..d692286 100644
--- a/sys/crypto/aesni/aesni.c
+++ b/sys/crypto/aesni/aesni.c
@@ -39,16 +39,34 @@ __FBSDID("$FreeBSD$");
#include <sys/rwlock.h>
#include <sys/bus.h>
#include <sys/uio.h>
+#include <sys/smp.h>
#include <crypto/aesni/aesni.h>
#include <cryptodev_if.h>
+static struct mtx_padalign *ctx_mtx;
+static struct fpu_kern_ctx **ctx_fpu;
+
struct aesni_softc {
+ int dieing;
int32_t cid;
uint32_t sid;
TAILQ_HEAD(aesni_sessions_head, aesni_session) sessions;
struct rwlock lock;
};
+#define AQUIRE_CTX(i, ctx) \
+ do { \
+ (i) = PCPU_GET(cpuid); \
+ mtx_lock(&ctx_mtx[(i)]); \
+ (ctx) = ctx_fpu[(i)]; \
+ } while (0)
+#define RELEASE_CTX(i, ctx) \
+ do { \
+ mtx_unlock(&ctx_mtx[(i)]); \
+ (i) = -1; \
+ (ctx) = NULL; \
+ } while (0)
+
static int aesni_newsession(device_t, uint32_t *sidp, struct cryptoini *cri);
static int aesni_freesession(device_t, uint64_t tid);
static void aesni_freesession_locked(struct aesni_softc *sc,
@@ -88,14 +106,36 @@ aesni_probe(device_t dev)
return (0);
}
+static void
+aensi_cleanctx(void)
+{
+ int i;
+
+ /* XXX - no way to return driverid */
+ CPU_FOREACH(i) {
+ if (ctx_fpu[i] != NULL) {
+ mtx_destroy(&ctx_mtx[i]);
+ fpu_kern_free_ctx(ctx_fpu[i]);
+ }
+ ctx_fpu[i] = NULL;
+ }
+ free(ctx_mtx, M_AESNI);
+ ctx_mtx = NULL;
+ free(ctx_fpu, M_AESNI);
+ ctx_fpu = NULL;
+}
+
static int
aesni_attach(device_t dev)
{
struct aesni_softc *sc;
+ int i;
sc = device_get_softc(dev);
+ sc->dieing = 0;
TAILQ_INIT(&sc->sessions);
sc->sid = 1;
+
sc->cid = crypto_get_driverid(dev, CRYPTOCAP_F_HARDWARE |
CRYPTOCAP_F_SYNC);
if (sc->cid < 0) {
@@ -103,6 +143,16 @@ aesni_attach(device_t dev)
return (ENOMEM);
}
+ ctx_mtx = malloc(sizeof *ctx_mtx * (mp_maxid + 1), M_AESNI,
+ M_WAITOK|M_ZERO);
+ ctx_fpu = malloc(sizeof *ctx_fpu * (mp_maxid + 1), M_AESNI,
+ M_WAITOK|M_ZERO);
+
+ CPU_FOREACH(i) {
+ ctx_fpu[i] = fpu_kern_alloc_ctx(0);
+ mtx_init(&ctx_mtx[i], "anifpumtx", NULL, MTX_DEF|MTX_NEW);
+ }
+
rw_init(&sc->lock, "aesni_lock");
crypto_register(sc->cid, CRYPTO_AES_CBC, 0, 0);
crypto_register(sc->cid, CRYPTO_AES_XTS, 0, 0);
@@ -116,6 +166,7 @@ aesni_detach(device_t dev)
struct aesni_session *ses;
sc = device_get_softc(dev);
+
rw_wlock(&sc->lock);
TAILQ_FOREACH(ses, &sc->sessions, next) {
if (ses->used) {
@@ -125,14 +176,18 @@ aesni_detach(device_t dev)
return (EBUSY);
}
}
+ sc->dieing = 1;
while ((ses = TAILQ_FIRST(&sc->sessions)) != NULL) {
TAILQ_REMOVE(&sc->sessions, ses, next);
- fpu_kern_free_ctx(ses->fpu_ctx);
free(ses, M_AESNI);
}
rw_wunlock(&sc->lock);
- rw_destroy(&sc->lock);
crypto_unregister_all(sc->cid);
+
+ rw_destroy(&sc->lock);
+
+ aensi_cleanctx();
+
return (0);
}
@@ -148,6 +203,9 @@ aesni_newsession(device_t dev, uint32_t *sidp, struct cryptoini *cri)
return (EINVAL);
sc = device_get_softc(dev);
+ if (sc->dieing)
+ return (EINVAL);
+
ses = NULL;
encini = NULL;
for (; cri != NULL; cri = cri->cri_next) {
@@ -166,6 +224,10 @@ aesni_newsession(device_t dev, uint32_t *sidp, struct cryptoini *cri)
return (EINVAL);
rw_wlock(&sc->lock);
+ if (sc->dieing) {
+ rw_wunlock(&sc->lock);
+ return (EINVAL);
+ }
/*
* Free sessions goes first, so if first session is used, we need to
* allocate one.
@@ -177,13 +239,6 @@ aesni_newsession(device_t dev, uint32_t *sidp, struct cryptoini *cri)
rw_wunlock(&sc->lock);
return (ENOMEM);
}
- ses->fpu_ctx = fpu_kern_alloc_ctx(FPU_KERN_NORMAL |
- FPU_KERN_NOWAIT);
- if (ses->fpu_ctx == NULL) {
- free(ses, M_AESNI);
- rw_wunlock(&sc->lock);
- return (ENOMEM);
- }
ses->id = sc->sid++;
} else {
TAILQ_REMOVE(&sc->sessions, ses, next);
@@ -208,15 +263,14 @@ aesni_newsession(device_t dev, uint32_t *sidp, struct cryptoini *cri)
static void
aesni_freesession_locked(struct aesni_softc *sc, struct aesni_session *ses)
{
- struct fpu_kern_ctx *ctx;
uint32_t sid;
+ rw_assert(&sc->lock, RA_WLOCKED);
+
sid = ses->id;
TAILQ_REMOVE(&sc->sessions, ses, next);
- ctx = ses->fpu_ctx;
bzero(ses, sizeof(*ses));
ses->id = sid;
- ses->fpu_ctx = ctx;
TAILQ_INSERT_HEAD(&sc->sessions, ses, next);
}
@@ -362,17 +416,27 @@ MODULE_DEPEND(aesni, crypto, 1, 1, 1);
static int
aesni_cipher_setup(struct aesni_session *ses, struct cryptoini *encini)
{
- struct thread *td;
+ struct fpu_kern_ctx *ctx;
int error;
+ int kt, ctxidx;
+
+ kt = is_fpu_kern_thread(0);
+ if (!kt) {
+ AQUIRE_CTX(ctxidx, ctx);
+ error = fpu_kern_enter(curthread, ctx,
+ FPU_KERN_NORMAL | FPU_KERN_KTHR);
+ if (error != 0)
+ goto out;
+ }
- td = curthread;
- error = fpu_kern_enter(td, ses->fpu_ctx, FPU_KERN_NORMAL |
- FPU_KERN_KTHR);
- if (error != 0)
- return (error);
error = aesni_cipher_setup_common(ses, encini->cri_key,
encini->cri_klen);
- fpu_kern_leave(td, ses->fpu_ctx);
+
+ if (!kt) {
+ fpu_kern_leave(curthread, ctx);
+out:
+ RELEASE_CTX(ctxidx, ctx);
+ }
return (error);
}
@@ -380,19 +444,24 @@ static int
aesni_cipher_process(struct aesni_session *ses, struct cryptodesc *enccrd,
struct cryptop *crp)
{
- struct thread *td;
+ struct fpu_kern_ctx *ctx;
uint8_t *buf;
int error, allocated;
+ int kt, ctxidx;
buf = aesni_cipher_alloc(enccrd, crp, &allocated);
if (buf == NULL)
return (ENOMEM);
- td = curthread;
- error = fpu_kern_enter(td, ses->fpu_ctx, FPU_KERN_NORMAL |
- FPU_KERN_KTHR);
- if (error != 0)
- goto out1;
+ error = 0;
+ kt = is_fpu_kern_thread(0);
+ if (!kt) {
+ AQUIRE_CTX(ctxidx, ctx);
+ error = fpu_kern_enter(curthread, ctx,
+ FPU_KERN_NORMAL|FPU_KERN_KTHR);
+ if (error != 0)
+ goto out2;
+ }
if ((enccrd->crd_flags & CRD_F_KEY_EXPLICIT) != 0) {
error = aesni_cipher_setup_common(ses, enccrd->crd_key,
@@ -438,8 +507,12 @@ aesni_cipher_process(struct aesni_session *ses, struct cryptodesc *enccrd,
enccrd->crd_skip + enccrd->crd_len - AES_BLOCK_LEN,
AES_BLOCK_LEN, ses->iv);
out:
- fpu_kern_leave(td, ses->fpu_ctx);
-out1:
+ if (!kt) {
+ fpu_kern_leave(curthread, ctx);
+out2:
+ RELEASE_CTX(ctxidx, ctx);
+ }
+
if (allocated) {
bzero(buf, enccrd->crd_len);
free(buf, M_AESNI);
diff --git a/sys/crypto/aesni/aesni.h b/sys/crypto/aesni/aesni.h
index ff1d1a2..dfca82a 100644
--- a/sys/crypto/aesni/aesni.h
+++ b/sys/crypto/aesni/aesni.h
@@ -65,7 +65,6 @@ struct aesni_session {
int used;
uint32_t id;
TAILQ_ENTRY(aesni_session) next;
- struct fpu_kern_ctx *fpu_ctx;
};
/*
OpenPOWER on IntegriCloud