ext4 crypto: allocate the right amount of memory for the on-disk symlink
authorTheodore Ts'o <tytso@mit.edu>
Sun, 31 May 2015 17:35:32 +0000 (13:35 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Sun, 31 May 2015 17:35:32 +0000 (13:35 -0400)
Previously we were taking the required padding when allocating space
for the on-disk symlink.  This caused a buffer overrun which could
trigger a krenel crash when running fsstress.

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
fs/ext4/crypto_fname.c
fs/ext4/ext4.h
fs/ext4/namei.c

index 23af41f73e90a663bc33f17c57ec33297517dc20..7dc4eb55913c445c5065646c99949b66d87979b5 100644 (file)
@@ -262,8 +262,20 @@ u32 ext4_fname_crypto_round_up(u32 size, u32 blksize)
        return ((size+blksize-1)/blksize)*blksize;
 }
 
-/**
- * ext4_fname_crypto_alloc_obuff() -
+unsigned ext4_fname_encrypted_size(struct inode *inode, u32 ilen)
+{
+       struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info;
+       int padding = 32;
+
+       if (ci)
+               padding = 4 << (ci->ci_flags & EXT4_POLICY_FLAGS_PAD_MASK);
+       if (ilen < EXT4_CRYPTO_BLOCK_SIZE)
+               ilen = EXT4_CRYPTO_BLOCK_SIZE;
+       return ext4_fname_crypto_round_up(ilen, padding);
+}
+
+/*
+ * ext4_fname_crypto_alloc_buffer() -
  *
  * Allocates an output buffer that is sufficient for the crypto operation
  * specified by the context and the direction.
@@ -271,15 +283,8 @@ u32 ext4_fname_crypto_round_up(u32 size, u32 blksize)
 int ext4_fname_crypto_alloc_buffer(struct inode *inode,
                                   u32 ilen, struct ext4_str *crypto_str)
 {
-       unsigned int olen;
-       int padding = 16;
-       struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info;
+       unsigned int olen = ext4_fname_encrypted_size(inode, ilen);
 
-       if (ci)
-               padding = 4 << (ci->ci_flags & EXT4_POLICY_FLAGS_PAD_MASK);
-       if (padding < EXT4_CRYPTO_BLOCK_SIZE)
-               padding = EXT4_CRYPTO_BLOCK_SIZE;
-       olen = ext4_fname_crypto_round_up(ilen, padding);
        crypto_str->len = olen;
        if (olen < EXT4_FNAME_CRYPTO_DIGEST_SIZE*2)
                olen = EXT4_FNAME_CRYPTO_DIGEST_SIZE*2;
index bd8d32d5214d6185dcbef3b709901a6734510ee5..730c88d1b220b3cd3af030f67f69c6f5094ecda8 100644 (file)
@@ -2090,6 +2090,7 @@ static inline int ext4_sb_has_crypto(struct super_block *sb)
 /* crypto_fname.c */
 bool ext4_valid_filenames_enc_mode(uint32_t mode);
 u32 ext4_fname_crypto_round_up(u32 size, u32 blksize);
+unsigned ext4_fname_encrypted_size(struct inode *inode, u32 ilen);
 int ext4_fname_crypto_alloc_buffer(struct inode *inode,
                                   u32 ilen, struct ext4_str *crypto_str);
 int _ext4_fname_disk_to_usr(struct inode *inode,
index 401b099e3af3c5de4fd72ed549aa0bdc0ad65ce6..bda4a5d6f5a58610e369ec7cd27a730c3dc6cc2b 100644 (file)
@@ -3039,10 +3039,23 @@ static int ext4_symlink(struct inode *dir,
 
        encryption_required = (ext4_encrypted_inode(dir) ||
                               DUMMY_ENCRYPTION_ENABLED(EXT4_SB(dir->i_sb)));
-       if (encryption_required)
-               disk_link.len = encrypted_symlink_data_len(len) + 1;
-       if (disk_link.len > dir->i_sb->s_blocksize)
-               return -ENAMETOOLONG;
+       if (encryption_required) {
+               err = ext4_get_encryption_info(dir);
+               if (err)
+                       return err;
+               if (ext4_encryption_info(dir) == NULL)
+                       return -EPERM;
+               disk_link.len = (ext4_fname_encrypted_size(dir, len) +
+                                sizeof(struct ext4_encrypted_symlink_data));
+               sd = kzalloc(disk_link.len, GFP_KERNEL);
+               if (!sd)
+                       return -ENOMEM;
+       }
+
+       if (disk_link.len > dir->i_sb->s_blocksize) {
+               err = -ENAMETOOLONG;
+               goto err_free_sd;
+       }
 
        dquot_initialize(dir);
 
@@ -3073,18 +3086,14 @@ static int ext4_symlink(struct inode *dir,
        if (IS_ERR(inode)) {
                if (handle)
                        ext4_journal_stop(handle);
-               return PTR_ERR(inode);
+               err = PTR_ERR(inode);
+               goto err_free_sd;
        }
 
        if (encryption_required) {
                struct qstr istr;
                struct ext4_str ostr;
 
-               sd = kzalloc(disk_link.len, GFP_NOFS);
-               if (!sd) {
-                       err = -ENOMEM;
-                       goto err_drop_inode;
-               }
                istr.name = (const unsigned char *) symname;
                istr.len = len;
                ostr.name = sd->encrypted_path;
@@ -3156,10 +3165,11 @@ static int ext4_symlink(struct inode *dir,
 err_drop_inode:
        if (handle)
                ext4_journal_stop(handle);
-       kfree(sd);
        clear_nlink(inode);
        unlock_new_inode(inode);
        iput(inode);
+err_free_sd:
+       kfree(sd);
        return err;
 }