Merge "ext4: check if in-inode xattr is corrupted in ext4_expand_extra_isize_ea()"
This commit is contained in:
commit
c6855ffd7f
5 changed files with 114 additions and 6 deletions
|
@ -469,3 +469,59 @@ uint32_t ext4_validate_encryption_key_size(uint32_t mode, uint32_t size)
|
|||
return size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate dentries for encrypted directories to make sure we aren't
|
||||
* potentially caching stale data after a key has been added or
|
||||
* removed.
|
||||
*/
|
||||
static int ext4_d_revalidate(struct dentry *dentry, unsigned int flags)
|
||||
{
|
||||
struct inode *dir = d_inode(dentry->d_parent);
|
||||
struct ext4_crypt_info *ci = EXT4_I(dir)->i_crypt_info;
|
||||
int dir_has_key, cached_with_key;
|
||||
|
||||
if (!ext4_encrypted_inode(dir))
|
||||
return 0;
|
||||
|
||||
if (ci && ci->ci_keyring_key &&
|
||||
(ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) |
|
||||
(1 << KEY_FLAG_REVOKED) |
|
||||
(1 << KEY_FLAG_DEAD))))
|
||||
ci = NULL;
|
||||
|
||||
/* this should eventually be an flag in d_flags */
|
||||
cached_with_key = dentry->d_fsdata != NULL;
|
||||
dir_has_key = (ci != NULL);
|
||||
|
||||
/*
|
||||
* If the dentry was cached without the key, and it is a
|
||||
* negative dentry, it might be a valid name. We can't check
|
||||
* if the key has since been made available due to locking
|
||||
* reasons, so we fail the validation so ext4_lookup() can do
|
||||
* this check.
|
||||
*
|
||||
* We also fail the validation if the dentry was created with
|
||||
* the key present, but we no longer have the key, or vice versa.
|
||||
*/
|
||||
if ((!cached_with_key && d_is_negative(dentry)) ||
|
||||
(!cached_with_key && dir_has_key) ||
|
||||
(cached_with_key && !dir_has_key)) {
|
||||
#if 0 /* Revalidation debug */
|
||||
char buf[80];
|
||||
char *cp = simple_dname(dentry, buf, sizeof(buf));
|
||||
|
||||
if (IS_ERR(cp))
|
||||
cp = (char *) "???";
|
||||
pr_err("revalidate: %s %p %d %d %d\n", cp, dentry->d_fsdata,
|
||||
cached_with_key, d_is_negative(dentry),
|
||||
dir_has_key);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
const struct dentry_operations ext4_encrypted_d_ops = {
|
||||
.d_revalidate = ext4_d_revalidate,
|
||||
};
|
||||
|
|
|
@ -111,6 +111,12 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
|
|||
int dir_has_error = 0;
|
||||
struct ext4_str fname_crypto_str = {.name = NULL, .len = 0};
|
||||
|
||||
if (ext4_encrypted_inode(inode)) {
|
||||
err = ext4_get_encryption_info(inode);
|
||||
if (err && err != -ENOKEY)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (is_dx_dir(inode)) {
|
||||
err = ext4_dx_readdir(file, ctx);
|
||||
if (err != ERR_BAD_DX_DIR) {
|
||||
|
@ -157,8 +163,11 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
|
|||
index, 1);
|
||||
file->f_ra.prev_pos = (loff_t)index << PAGE_CACHE_SHIFT;
|
||||
bh = ext4_bread(NULL, inode, map.m_lblk, 0);
|
||||
if (IS_ERR(bh))
|
||||
return PTR_ERR(bh);
|
||||
if (IS_ERR(bh)) {
|
||||
err = PTR_ERR(bh);
|
||||
bh = NULL;
|
||||
goto errout;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bh) {
|
||||
|
|
|
@ -2258,6 +2258,7 @@ struct page *ext4_encrypt(struct inode *inode,
|
|||
struct page *plaintext_page);
|
||||
int ext4_decrypt(struct page *page);
|
||||
int ext4_encrypted_zeroout(struct inode *inode, struct ext4_extent *ex);
|
||||
extern const struct dentry_operations ext4_encrypted_d_ops;
|
||||
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
int ext4_init_crypto(void);
|
||||
|
|
|
@ -1558,6 +1558,24 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi
|
|||
struct ext4_dir_entry_2 *de;
|
||||
struct buffer_head *bh;
|
||||
|
||||
if (ext4_encrypted_inode(dir)) {
|
||||
int res = ext4_get_encryption_info(dir);
|
||||
|
||||
/*
|
||||
* This should be a properly defined flag for
|
||||
* dentry->d_flags when we uplift this to the VFS.
|
||||
* d_fsdata is set to (void *) 1 if if the dentry is
|
||||
* created while the directory was encrypted and we
|
||||
* don't have access to the key.
|
||||
*/
|
||||
dentry->d_fsdata = NULL;
|
||||
if (ext4_encryption_info(dir))
|
||||
dentry->d_fsdata = (void *) 1;
|
||||
d_set_d_op(dentry, &ext4_encrypted_d_ops);
|
||||
if (res && res != -ENOKEY)
|
||||
return ERR_PTR(res);
|
||||
}
|
||||
|
||||
if (dentry->d_name.len > EXT4_NAME_LEN)
|
||||
return ERR_PTR(-ENAMETOOLONG);
|
||||
|
||||
|
|
|
@ -232,6 +232,27 @@ ext4_xattr_check_block(struct inode *inode, struct buffer_head *bh)
|
|||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
__xattr_check_inode(struct inode *inode, struct ext4_xattr_ibody_header *header,
|
||||
void *end, const char *function, unsigned int line)
|
||||
{
|
||||
struct ext4_xattr_entry *entry = IFIRST(header);
|
||||
int error = -EFSCORRUPTED;
|
||||
|
||||
if (((void *) header >= end) ||
|
||||
(header->h_magic != le32_to_cpu(EXT4_XATTR_MAGIC)))
|
||||
goto errout;
|
||||
error = ext4_xattr_check_names(entry, end, entry);
|
||||
errout:
|
||||
if (error)
|
||||
__ext4_error_inode(inode, function, line, 0,
|
||||
"corrupted in-inode xattr");
|
||||
return error;
|
||||
}
|
||||
|
||||
#define xattr_check_inode(inode, header, end) \
|
||||
__xattr_check_inode((inode), (header), (end), __func__, __LINE__)
|
||||
|
||||
static inline int
|
||||
ext4_xattr_check_entry(struct ext4_xattr_entry *entry, size_t size)
|
||||
{
|
||||
|
@ -343,7 +364,7 @@ ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name,
|
|||
header = IHDR(inode, raw_inode);
|
||||
entry = IFIRST(header);
|
||||
end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;
|
||||
error = ext4_xattr_check_names(entry, end, entry);
|
||||
error = xattr_check_inode(inode, header, end);
|
||||
if (error)
|
||||
goto cleanup;
|
||||
error = ext4_xattr_find_entry(&entry, name_index, name,
|
||||
|
@ -474,7 +495,7 @@ ext4_xattr_ibody_list(struct dentry *dentry, char *buffer, size_t buffer_size)
|
|||
raw_inode = ext4_raw_inode(&iloc);
|
||||
header = IHDR(inode, raw_inode);
|
||||
end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;
|
||||
error = ext4_xattr_check_names(IFIRST(header), end, IFIRST(header));
|
||||
error = xattr_check_inode(inode, header, end);
|
||||
if (error)
|
||||
goto cleanup;
|
||||
error = ext4_xattr_list_entries(dentry, IFIRST(header),
|
||||
|
@ -990,8 +1011,7 @@ int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
|
|||
is->s.here = is->s.first;
|
||||
is->s.end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;
|
||||
if (ext4_test_inode_state(inode, EXT4_STATE_XATTR)) {
|
||||
error = ext4_xattr_check_names(IFIRST(header), is->s.end,
|
||||
IFIRST(header));
|
||||
error = xattr_check_inode(inode, header, is->s.end);
|
||||
if (error)
|
||||
return error;
|
||||
/* Find the named attribute. */
|
||||
|
@ -1288,6 +1308,10 @@ retry:
|
|||
last = entry;
|
||||
total_ino = sizeof(struct ext4_xattr_ibody_header);
|
||||
|
||||
error = xattr_check_inode(inode, header, end);
|
||||
if (error)
|
||||
goto cleanup;
|
||||
|
||||
free = ext4_xattr_free_space(last, &min_offs, base, &total_ino);
|
||||
if (free >= new_extra_isize) {
|
||||
entry = IFIRST(header);
|
||||
|
|
Loading…
Add table
Reference in a new issue