CVE-2016-10154: Improper Restriction of Operations within the Bounds of a Memory Buffer (CWE-119)
value: The smbhash function in fs/cifs/smbencrypt.c in the Linux kernel 4.9.x before 4.9.1 interacts incorrectly with the CONFIG_VMAP_STACK option, which allows local users to cause a denial of service (system crash or memory corruption) or possibly have unspecified other impact by leveraging use of more than one virtual page for a scatterlist.
CWE description: The product performs operations on a memory buffer, but it reads from or writes to a memory location outside the buffer's intended boundary. This may result in read or write operations on unexpected memory locations that could be linked to other variables, data structures, or internal program data.
CVSS v3 base score: 5.5
Published: 2017-02-06T06:59Z
Repository: https://github.com/torvalds/linux
Language: C
Files changed: fs/cifs/smbencrypt.c
Commit date: 2016-12-14 01:44:16 -0600
Commit message: cifs: Fix smbencrypt() to stop pointing a scatterlist at the stack

smbencrypt() points a scatterlist to the stack, which is breaks if
CONFIG_VMAP_STACK=y.

Fix it by switching to crypto_cipher_encrypt_one().  The new code
should be considerably faster as an added benefit.

This code is nearly identical to some code that Eric Biggers
suggested.

Cc: stable@vger.kernel.org # 4.9 only
Reported-by: Eric Biggers <ebiggers3@gmail.com>
Signed-off-by: Andy Lutomirski <luto@kernel.org>
Acked-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <smfrench@gmail.com>

Diff: diff --git a/fs/cifs/smbencrypt.c b/fs/cifs/smbencrypt.c
index 699b7868108f65..c12bffefa3c9b5 100644
--- a/fs/cifs/smbencrypt.c
+++ b/fs/cifs/smbencrypt.c
@@ -21,11 +21,11 @@
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
-#include <crypto/skcipher.h>
+#include <linux/crypto.h>
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/fs.h>
 #include <linux/string.h>
 #include <linux/kernel.h>
@@ -67,50 +67,26 @@ str_to_key(unsigned char *str, unsigned char *key)
 }
 
 static int
 smbhash(unsigned char *out, const unsigned char *in, unsigned char *key)
 {
-	int rc;
 	unsigned char key2[8];
-	struct crypto_skcipher *tfm_des;
-	struct scatterlist sgin, sgout;
-	struct skcipher_request *req;
+	struct crypto_cipher *tfm_des;
 
 	str_to_key(key, key2);
 
-	tfm_des = crypto_alloc_skcipher("ecb(des)", 0, CRYPTO_ALG_ASYNC);
+	tfm_des = crypto_alloc_cipher("des", 0, 0);
 	if (IS_ERR(tfm_des)) {
-		rc = PTR_ERR(tfm_des);
-		cifs_dbg(VFS, "could not allocate des crypto API\n");
-		goto smbhash_err;
-	}
-
-	req = skcipher_request_alloc(tfm_des, GFP_KERNEL);
-	if (!req) {
-		rc = -ENOMEM;
 		cifs_dbg(VFS, "could not allocate des crypto API\n");
-		goto smbhash_free_skcipher;
+		return PTR_ERR(tfm_des);
 	}
 
-	crypto_skcipher_setkey(tfm_des, key2, 8);
-
-	sg_init_one(&sgin, in, 8);
-	sg_init_one(&sgout, out, 8);
+	crypto_cipher_setkey(tfm_des, key2, 8);
+	crypto_cipher_encrypt_one(tfm_des, out, in);
+	crypto_free_cipher(tfm_des);
 
-	skcipher_request_set_callback(req, 0, NULL, NULL);
-	skcipher_request_set_crypt(req, &sgin, &sgout, 8, NULL);
-
-	rc = crypto_skcipher_encrypt(req);
-	if (rc)
-		cifs_dbg(VFS, "could not encrypt crypt key rc: %d\n", rc);
-
-	skcipher_request_free(req);
-
-smbhash_free_skcipher:
-	crypto_free_skcipher(tfm_des);
-smbhash_err:
-	return rc;
+	return 0;
 }
 
 static int
 E_P16(unsigned char *p14, unsigned char *p16)
 {

Vulnerable code: #include <crypto/skcipher.h>
	int rc;
	struct crypto_skcipher *tfm_des;
	struct scatterlist sgin, sgout;
	struct skcipher_request *req;
	tfm_des = crypto_alloc_skcipher("ecb(des)", 0, CRYPTO_ALG_ASYNC);
		rc = PTR_ERR(tfm_des);
		cifs_dbg(VFS, "could not allocate des crypto API\n");
		goto smbhash_err;
	}

	req = skcipher_request_alloc(tfm_des, GFP_KERNEL);
	if (!req) {
		rc = -ENOMEM;
		goto smbhash_free_skcipher;
	crypto_skcipher_setkey(tfm_des, key2, 8);

	sg_init_one(&sgin, in, 8);
	sg_init_one(&sgout, out, 8);
	skcipher_request_set_callback(req, 0, NULL, NULL);
	skcipher_request_set_crypt(req, &sgin, &sgout, 8, NULL);

	rc = crypto_skcipher_encrypt(req);
	if (rc)
		cifs_dbg(VFS, "could not encrypt crypt key rc: %d\n", rc);

	skcipher_request_free(req);

smbhash_free_skcipher:
	crypto_free_skcipher(tfm_des);
smbhash_err:
	return rc;
Fixed code: #include <linux/crypto.h>
	struct crypto_cipher *tfm_des;
	tfm_des = crypto_alloc_cipher("des", 0, 0);
		return PTR_ERR(tfm_des);
	crypto_cipher_setkey(tfm_des, key2, 8);
	crypto_cipher_encrypt_one(tfm_des, out, in);
	crypto_free_cipher(tfm_des);
	return 0;