From 1a407a697d3d16eecff54e4d224305768bb2e337 Mon Sep 17 00:00:00 2001
From: Phil Sutter <phil.sutter@viprinet.com>
Date: Thu, 4 Nov 2010 17:31:14 +0100
Subject: [PATCH 111/291] cryptodev: make

Certain hardware crypto engines need input data to be correctly aligned.
Due to copyless operation, data alignment has to be done in userspace.
To assist in that, find out the smallest possible alignment boundary
upon session creation, depending on the actual cipher/hash mode(s) being
selected (and the automatically chosen crypto backend, of course).

Since now the information is present internally, check directly used
(zero-copy) userspace buffers for correct alignment. Handling of
temporary, internal buffers is unclear for now. Maybe one should align
them to a high value (e.g. 64 bytes) to be sure. Allocating everything
dynamically instead is probably not a good alternative.

The only buffers userspace applications have to align are the 'src' and
'dst' fields of struct crypt_op. All other userspace buffers are copied
internally, since their small size doesn't justify the overhead of
mapping userspace pages for them.

Signed-off-by: Nikos Mavrogiannopoulos <nmav@gnutls.org>
---
 drivers/staging/crypto/cryptodev/cryptodev.h        |  2 ++
 drivers/staging/crypto/cryptodev/cryptodev_cipher.c |  2 ++
 drivers/staging/crypto/cryptodev/cryptodev_int.h    |  4 ++++
 drivers/staging/crypto/cryptodev/cryptodev_main.c   | 18 +++++++++++++++++-
 4 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/crypto/cryptodev/cryptodev.h b/drivers/staging/crypto/cryptodev/cryptodev.h
index e16d30a06553..f367262be652 100644
--- a/drivers/staging/crypto/cryptodev/cryptodev.h
+++ b/drivers/staging/crypto/cryptodev/cryptodev.h
@@ -81,6 +81,8 @@ struct session_op {
 	__u8	__user *mackey;
 
 	__u32	ses;		/* session identifier */
+
+	__u16	alignmask;	/* alignment constraints */
 };
 
 #define	COP_ENCRYPT	0
diff --git a/drivers/staging/crypto/cryptodev/cryptodev_cipher.c b/drivers/staging/crypto/cryptodev/cryptodev_cipher.c
index 0dd3fba33910..0bce1b6f48c0 100644
--- a/drivers/staging/crypto/cryptodev/cryptodev_cipher.c
+++ b/drivers/staging/crypto/cryptodev/cryptodev_cipher.c
@@ -94,6 +94,7 @@ int cryptodev_cipher_init(struct cipher_data *out, const char *alg_name,
 
 	out->blocksize = crypto_ablkcipher_blocksize(out->async.s);
 	out->ivsize = crypto_ablkcipher_ivsize(out->async.s);
+	out->alignmask = crypto_ablkcipher_alignmask(out->async.s);
 
 	out->async.result = kmalloc(sizeof(*out->async.result), GFP_KERNEL);
 	if (unlikely(!out->async.result)) {
@@ -225,6 +226,7 @@ int cryptodev_hash_init(struct hash_data *hdata, const char *alg_name,
 	}
 
 	hdata->digestsize = crypto_ahash_digestsize(hdata->async.s);
+	hdata->alignmask = crypto_ahash_alignmask(hdata->async.s);
 
 	hdata->async.result = kmalloc(sizeof(*hdata->async.result), GFP_KERNEL);
 	if (unlikely(!hdata->async.result)) {
diff --git a/drivers/staging/crypto/cryptodev/cryptodev_int.h b/drivers/staging/crypto/cryptodev/cryptodev_int.h
index aacf6706d666..bdf5a46ee20c 100644
--- a/drivers/staging/crypto/cryptodev/cryptodev_int.h
+++ b/drivers/staging/crypto/cryptodev/cryptodev_int.h
@@ -41,6 +41,7 @@ struct cipher_data {
 	int init; /* 0 uninitialized */
 	int blocksize;
 	int ivsize;
+	int alignmask;
 	struct {
 		struct crypto_ablkcipher *s;
 		struct cryptodev_result *result;
@@ -69,6 +70,7 @@ inline static void cryptodev_cipher_set_iv(struct cipher_data *cdata,
 struct hash_data {
 	int init; /* 0 uninitialized */
 	int digestsize;
+	int alignmask;
 	struct {
 		struct crypto_ahash *s;
 		struct cryptodev_result *result;
@@ -101,6 +103,8 @@ struct compat_session_op {
 	compat_uptr_t	mackey;		/* pointer to mac key data */
 
 	uint32_t	ses;		/* session identifier */
+
+	uint16_t	alignmask;	/* alignment constraints */
 };
 
 /* input of CIOCCRYPT */
diff --git a/drivers/staging/crypto/cryptodev/cryptodev_main.c b/drivers/staging/crypto/cryptodev/cryptodev_main.c
index 7326c3de2d03..508c7f94d065 100644
--- a/drivers/staging/crypto/cryptodev/cryptodev_main.c
+++ b/drivers/staging/crypto/cryptodev/cryptodev_main.c
@@ -110,6 +110,7 @@ struct csession {
 	struct cipher_data cdata;
 	struct hash_data hdata;
 	uint32_t sid;
+	uint32_t alignmask;
 #ifdef CRYPTODEV_STATS
 #if !((COP_ENCRYPT < 2) && (COP_DECRYPT < 2))
 #error Struct csession.stat uses COP_{ENCRYPT,DECRYPT} as indices. Do something!
@@ -272,7 +273,7 @@ crypto_create_session(struct fcrypt *fcr, struct session_op *sop)
 			goto error_hash;
 		}
 
-		if (unlikely(copy_from_user(keyp, sop->mackey,
+		if (sop->mackey && unlikely(copy_from_user(keyp, sop->mackey,
 					    sop->mackeylen))) {
 			ret = -EFAULT;
 			goto error_hash;
@@ -289,6 +290,10 @@ crypto_create_session(struct fcrypt *fcr, struct session_op *sop)
 		}
 	}
 
+	sop->alignmask = ses_new->alignmask = max(ses_new->cdata.alignmask,
+	                                          ses_new->hdata.alignmask);
+	dprintk(2, KERN_DEBUG, "%s: got alignmask %d\n", __func__, ses_new->alignmask);
+
 	ses_new->array_size = DEFAULT_PREALLOC_PAGES;
 	dprintk(2, KERN_DEBUG, "%s: preallocating for %d user pages\n",
 			__func__, ses_new->array_size);
@@ -588,6 +593,11 @@ static int get_userbuf(struct csession *ses, struct kernel_crypt_op *kcop,
 	if (cop->src == NULL)
 		return -EINVAL;
 
+	if (!IS_ALIGNED((unsigned long)cop->src, ses->alignmask)) {
+		dprintk(2, KERN_WARNING, "%s: careful - source address %lx is not %d byte aligned\n",
+				__func__, (unsigned long)cop->src, ses->alignmask + 1);
+	}
+
 	src_pagecount = PAGECOUNT(cop->src, cop->len);
 	if (!ses->cdata.init) {		/* hashing only */
 		write_src = 0;
@@ -597,6 +607,12 @@ static int get_userbuf(struct csession *ses, struct kernel_crypt_op *kcop,
 
 		dst_pagecount = PAGECOUNT(cop->dst, cop->len);
 		write_src = 0;
+
+		if (!IS_ALIGNED((unsigned long)cop->dst, ses->alignmask)) {
+			dprintk(2, KERN_WARNING, "%s: careful - destination address %lx is not %d byte aligned\n",
+					__func__, (unsigned long)cop->dst, ses->alignmask + 1);
+		}
+
 	}
 	(*tot_pages) = pagecount = src_pagecount + dst_pagecount;
 
-- 
2.1.0

