CVE-2016-10147: NULL Pointer Dereference (CWE-476)
value: crypto/mcryptd.c in the Linux kernel before 4.8.15 allows local users to cause a denial of service (NULL pointer dereference and system crash) by using an AF_ALG socket with an incompatible algorithm, as demonstrated by mcryptd(md5).
CWE description: The product dereferences a pointer that it expects to be valid but is NULL.
CVSS v3 base score: 5.5
Published: 2017-01-18T21:59Z
Repository: https://github.com/torvalds/linux
Language: C
Files changed: crypto/mcryptd.c
Commit date: 2016-12-07 19:55:37 +0800
Commit message: crypto: mcryptd - Check mcryptd algorithm compatibility

Algorithms not compatible with mcryptd could be spawned by mcryptd
with a direct crypto_alloc_tfm invocation using a "mcryptd(alg)" name
construct.  This causes mcryptd to crash the kernel if an arbitrary
"alg" is incompatible and not intended to be used with mcryptd.  It is
an issue if AF_ALG tries to spawn mcryptd(alg) to expose it externally.
But such algorithms must be used internally and not be exposed.

We added a check to enforce that only internal algorithms are allowed
with mcryptd at the time mcryptd is spawning an algorithm.

Link: http://marc.info/?l=linux-crypto-vger&m=148063683310477&w=2
Cc: stable@vger.kernel.org
Reported-by: Mikulas Patocka <mpatocka@redhat.com>
Signed-off-by: Tim Chen <tim.c.chen@linux.intel.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

Diff: diff --git a/crypto/mcryptd.c b/crypto/mcryptd.c
index 94ee44acd46568..c207458d629933 100644
--- a/crypto/mcryptd.c
+++ b/crypto/mcryptd.c
@@ -252,22 +252,26 @@ static void *mcryptd_alloc_instance(struct crypto_alg *alg, unsigned int head,
 	kfree(p);
 	p = ERR_PTR(err);
 	goto out;
 }
 
-static inline void mcryptd_check_internal(struct rtattr **tb, u32 *type,
+static inline bool mcryptd_check_internal(struct rtattr **tb, u32 *type,
 					  u32 *mask)
 {
 	struct crypto_attr_type *algt;
 
 	algt = crypto_get_attr_type(tb);
 	if (IS_ERR(algt))
-		return;
-	if ((algt->type & CRYPTO_ALG_INTERNAL))
-		*type |= CRYPTO_ALG_INTERNAL;
-	if ((algt->mask & CRYPTO_ALG_INTERNAL))
-		*mask |= CRYPTO_ALG_INTERNAL;
+		return false;
+
+	*type |= algt->type & CRYPTO_ALG_INTERNAL;
+	*mask |= algt->mask & CRYPTO_ALG_INTERNAL;
+
+	if (*type & *mask & CRYPTO_ALG_INTERNAL)
+		return true;
+	else
+		return false;
 }
 
 static int mcryptd_hash_init_tfm(struct crypto_tfm *tfm)
 {
 	struct crypto_instance *inst = crypto_tfm_alg_instance(tfm);
@@ -490,11 +494,12 @@ static int mcryptd_create_hash(struct crypto_template *tmpl, struct rtattr **tb,
 	struct crypto_alg *alg;
 	u32 type = 0;
 	u32 mask = 0;
 	int err;
 
-	mcryptd_check_internal(tb, &type, &mask);
+	if (!mcryptd_check_internal(tb, &type, &mask))
+		return -EINVAL;
 
 	halg = ahash_attr_alg(tb[1], type, mask);
 	if (IS_ERR(halg))
 		return PTR_ERR(halg);
 

Vulnerable code: static inline void mcryptd_check_internal(struct rtattr **tb, u32 *type,
		return;
	if ((algt->type & CRYPTO_ALG_INTERNAL))
		*type |= CRYPTO_ALG_INTERNAL;
	if ((algt->mask & CRYPTO_ALG_INTERNAL))
		*mask |= CRYPTO_ALG_INTERNAL;
	mcryptd_check_internal(tb, &type, &mask);
Fixed code: static inline bool mcryptd_check_internal(struct rtattr **tb, u32 *type,
		return false;

	*type |= algt->type & CRYPTO_ALG_INTERNAL;
	*mask |= algt->mask & CRYPTO_ALG_INTERNAL;

	if (*type & *mask & CRYPTO_ALG_INTERNAL)
		return true;
	else
		return false;
	if (!mcryptd_check_internal(tb, &type, &mask))
		return -EINVAL;