Changeset View
Changeset View
Standalone View
Standalone View
crypto/aesni/aesni.c
Show First 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | |||||
static struct fpu_kern_ctx **ctx_fpu; | static struct fpu_kern_ctx **ctx_fpu; | ||||
struct aesni_softc { | struct aesni_softc { | ||||
int dieing; | int dieing; | ||||
int32_t cid; | int32_t cid; | ||||
uint32_t sid; | uint32_t sid; | ||||
bool has_aes; | bool has_aes; | ||||
bool has_sha; | bool has_sha; | ||||
TAILQ_HEAD(aesni_sessions_head, aesni_session) sessions; | TAILQ_HEAD(aesni_sessions_head, aesni_session) free_sessions; | ||||
RB_HEAD(aesni_session_tree, aesni_session) sessions; | |||||
struct rwlock lock; | struct rwlock lock; | ||||
}; | }; | ||||
static __inline int | |||||
aesni_session_id_cmp(struct aesni_session *a, struct aesni_session *b) | |||||
{ | |||||
if (a->id > b->id) | |||||
return (1); | |||||
else if (a->id < b->id) | |||||
return (-1); | |||||
return (0); | |||||
} | |||||
RB_GENERATE_STATIC(aesni_session_tree, aesni_session, entry, aesni_session_id_cmp); | |||||
#define ACQUIRE_CTX(i, ctx) \ | #define ACQUIRE_CTX(i, ctx) \ | ||||
do { \ | do { \ | ||||
(i) = PCPU_GET(cpuid); \ | (i) = PCPU_GET(cpuid); \ | ||||
mtx_lock(&ctx_mtx[(i)]); \ | mtx_lock(&ctx_mtx[(i)]); \ | ||||
(ctx) = ctx_fpu[(i)]; \ | (ctx) = ctx_fpu[(i)]; \ | ||||
} while (0) | } while (0) | ||||
#define RELEASE_CTX(i, ctx) \ | #define RELEASE_CTX(i, ctx) \ | ||||
do { \ | do { \ | ||||
▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | |||||
static int | static int | ||||
aesni_attach(device_t dev) | aesni_attach(device_t dev) | ||||
{ | { | ||||
struct aesni_softc *sc; | struct aesni_softc *sc; | ||||
int i; | int i; | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
sc->dieing = 0; | sc->dieing = 0; | ||||
TAILQ_INIT(&sc->sessions); | TAILQ_INIT(&sc->free_sessions); | ||||
RB_INIT(&sc->sessions); | |||||
sc->sid = 1; | sc->sid = 1; | ||||
sc->cid = crypto_get_driverid(dev, CRYPTOCAP_F_HARDWARE | | sc->cid = crypto_get_driverid(dev, CRYPTOCAP_F_HARDWARE | | ||||
CRYPTOCAP_F_SYNC); | CRYPTOCAP_F_SYNC); | ||||
if (sc->cid < 0) { | if (sc->cid < 0) { | ||||
device_printf(dev, "Could not get crypto driver id.\n"); | device_printf(dev, "Could not get crypto driver id.\n"); | ||||
return (ENOMEM); | return (ENOMEM); | ||||
} | } | ||||
Show All 32 Lines | |||||
aesni_detach(device_t dev) | aesni_detach(device_t dev) | ||||
{ | { | ||||
struct aesni_softc *sc; | struct aesni_softc *sc; | ||||
struct aesni_session *ses; | struct aesni_session *ses; | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
rw_wlock(&sc->lock); | rw_wlock(&sc->lock); | ||||
TAILQ_FOREACH(ses, &sc->sessions, next) { | if (!RB_EMPTY(&sc->sessions)) { | ||||
if (ses->used) { | |||||
rw_wunlock(&sc->lock); | rw_wunlock(&sc->lock); | ||||
device_printf(dev, | device_printf(dev, | ||||
"Cannot detach, sessions still active.\n"); | "Cannot detach, sessions still active.\n"); | ||||
return (EBUSY); | return (EBUSY); | ||||
} | } | ||||
} | |||||
sc->dieing = 1; | sc->dieing = 1; | ||||
while ((ses = TAILQ_FIRST(&sc->sessions)) != NULL) { | while ((ses = TAILQ_FIRST(&sc->free_sessions)) != NULL) { | ||||
TAILQ_REMOVE(&sc->sessions, ses, next); | TAILQ_REMOVE(&sc->free_sessions, ses, next); | ||||
free(ses, M_AESNI); | free(ses, M_AESNI); | ||||
} | } | ||||
rw_wunlock(&sc->lock); | rw_wunlock(&sc->lock); | ||||
cem: This replacement is wrong. It can result in use-after-free. The function must abort like it… | |||||
Done Inline ActionsYes my bad! emeric.poupon_stormshield.eu: Yes my bad! | |||||
crypto_unregister_all(sc->cid); | crypto_unregister_all(sc->cid); | ||||
rw_destroy(&sc->lock); | rw_destroy(&sc->lock); | ||||
aesni_cleanctx(); | aesni_cleanctx(); | ||||
return (0); | return (0); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 74 Lines • ▼ Show 20 Lines | unhandled: | ||||
if (gcm_hash != gcm) | if (gcm_hash != gcm) | ||||
return (EINVAL); | return (EINVAL); | ||||
rw_wlock(&sc->lock); | rw_wlock(&sc->lock); | ||||
if (sc->dieing) { | if (sc->dieing) { | ||||
rw_wunlock(&sc->lock); | rw_wunlock(&sc->lock); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
/* | |||||
* Free sessions are inserted at the head of the list. So if the first | ses = TAILQ_FIRST(&sc->free_sessions); | ||||
* session is used, none are free and we must allocate a new one. | if (ses == NULL) { | ||||
*/ | |||||
ses = TAILQ_FIRST(&sc->sessions); | |||||
if (ses == NULL || ses->used) { | |||||
ses = malloc(sizeof(*ses), M_AESNI, M_NOWAIT | M_ZERO); | ses = malloc(sizeof(*ses), M_AESNI, M_NOWAIT | M_ZERO); | ||||
if (ses == NULL) { | if (ses == NULL) { | ||||
rw_wunlock(&sc->lock); | rw_wunlock(&sc->lock); | ||||
return (ENOMEM); | return (ENOMEM); | ||||
} | } | ||||
ses->id = sc->sid++; | ses->id = sc->sid++; | ||||
} else { | } else { | ||||
TAILQ_REMOVE(&sc->sessions, ses, next); | TAILQ_REMOVE(&sc->free_sessions, ses, next); | ||||
} | } | ||||
ses->used = 1; | RB_INSERT(aesni_session_tree, &sc->sessions, ses); | ||||
TAILQ_INSERT_TAIL(&sc->sessions, ses, next); | |||||
rw_wunlock(&sc->lock); | rw_wunlock(&sc->lock); | ||||
if (encini != NULL) | if (encini != NULL) | ||||
ses->algo = encini->cri_alg; | ses->algo = encini->cri_alg; | ||||
if (authini != NULL) | if (authini != NULL) | ||||
ses->auth_algo = authini->cri_alg; | ses->auth_algo = authini->cri_alg; | ||||
error = aesni_cipher_setup(ses, encini, authini); | error = aesni_cipher_setup(ses, encini, authini); | ||||
Show All 12 Lines | |||||
static void | static void | ||||
aesni_freesession_locked(struct aesni_softc *sc, struct aesni_session *ses) | aesni_freesession_locked(struct aesni_softc *sc, struct aesni_session *ses) | ||||
{ | { | ||||
uint32_t sid; | uint32_t sid; | ||||
rw_assert(&sc->lock, RA_WLOCKED); | rw_assert(&sc->lock, RA_WLOCKED); | ||||
sid = ses->id; | sid = ses->id; | ||||
TAILQ_REMOVE(&sc->sessions, ses, next); | RB_REMOVE(aesni_session_tree, &sc->sessions, ses); | ||||
explicit_bzero(ses, sizeof(*ses)); | explicit_bzero(ses, sizeof(*ses)); | ||||
ses->id = sid; | ses->id = sid; | ||||
TAILQ_INSERT_HEAD(&sc->sessions, ses, next); | TAILQ_INSERT_HEAD(&sc->free_sessions, ses, next); | ||||
} | } | ||||
static int | static int | ||||
aesni_freesession(device_t dev, uint64_t tid) | aesni_freesession(device_t dev, uint64_t tid) | ||||
{ | { | ||||
struct aesni_softc *sc; | struct aesni_softc *sc; | ||||
struct aesni_session *ses; | struct aesni_session *ses; | ||||
uint32_t sid; | struct aesni_session find; | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
sid = ((uint32_t)tid) & 0xffffffff; | find.id = CRYPTO_SESID2LID(tid); | ||||
rw_wlock(&sc->lock); | rw_wlock(&sc->lock); | ||||
TAILQ_FOREACH_REVERSE(ses, &sc->sessions, aesni_sessions_head, next) { | ses = RB_FIND(aesni_session_tree, &sc->sessions, &find); | ||||
if (ses->id == sid) | |||||
break; | |||||
} | |||||
if (ses == NULL) { | if (ses == NULL) { | ||||
rw_wunlock(&sc->lock); | rw_wunlock(&sc->lock); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
aesni_freesession_locked(sc, ses); | aesni_freesession_locked(sc, ses); | ||||
rw_wunlock(&sc->lock); | rw_wunlock(&sc->lock); | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
aesni_process(device_t dev, struct cryptop *crp, int hint __unused) | aesni_process(device_t dev, struct cryptop *crp, int hint __unused) | ||||
{ | { | ||||
struct aesni_softc *sc; | struct aesni_softc *sc; | ||||
struct aesni_session *ses; | struct aesni_session *ses; | ||||
struct aesni_session find; | |||||
struct cryptodesc *crd, *enccrd, *authcrd; | struct cryptodesc *crd, *enccrd, *authcrd; | ||||
int error, needauth; | int error, needauth; | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
ses = NULL; | ses = NULL; | ||||
error = 0; | error = 0; | ||||
enccrd = NULL; | enccrd = NULL; | ||||
authcrd = NULL; | authcrd = NULL; | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | aesni_process(device_t dev, struct cryptop *crp, int hint __unused) | ||||
/* CBC & XTS can only handle full blocks for now */ | /* CBC & XTS can only handle full blocks for now */ | ||||
if (enccrd != NULL && (enccrd->crd_alg == CRYPTO_AES_CBC || | if (enccrd != NULL && (enccrd->crd_alg == CRYPTO_AES_CBC || | ||||
enccrd->crd_alg == CRYPTO_AES_XTS) && | enccrd->crd_alg == CRYPTO_AES_XTS) && | ||||
(enccrd->crd_len % AES_BLOCK_LEN) != 0) { | (enccrd->crd_len % AES_BLOCK_LEN) != 0) { | ||||
error = EINVAL; | error = EINVAL; | ||||
goto out; | goto out; | ||||
} | } | ||||
find.id = CRYPTO_SESID2LID(crp->crp_sid); | |||||
rw_rlock(&sc->lock); | rw_rlock(&sc->lock); | ||||
TAILQ_FOREACH_REVERSE(ses, &sc->sessions, aesni_sessions_head, next) { | ses = RB_FIND(aesni_session_tree, &sc->sessions, &find); | ||||
if (ses->id == (crp->crp_sid & 0xffffffff)) | |||||
break; | |||||
} | |||||
rw_runlock(&sc->lock); | rw_runlock(&sc->lock); | ||||
if (ses == NULL) { | if (ses == NULL) { | ||||
error = EINVAL; | error = EINVAL; | ||||
goto out; | goto out; | ||||
} | } | ||||
error = aesni_cipher_process(ses, enccrd, authcrd, crp); | error = aesni_cipher_process(ses, enccrd, authcrd, crp); | ||||
if (error != 0) | if (error != 0) | ||||
▲ Show 20 Lines • Show All 468 Lines • Show Last 20 Lines |
This replacement is wrong. It can result in use-after-free. The function must abort like it did before if there are any entries in sc->sessions (i.e., any active sessions). It cannot free active sessions.