diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index 5a6fe1513fd2..ea20e360dc97 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -1019,7 +1019,8 @@ static int ext4_xattr_inode_update_ref(handle_t *handle, struct inode *ea_inode, int ref_change) { struct ext4_iloc iloc; - s64 ref_count; + u64 ref_count; + u64 new_count; int ret; inode_lock_nested(ea_inode, I_MUTEX_XATTR); @@ -1029,29 +1030,39 @@ static int ext4_xattr_inode_update_ref(handle_t *handle, struct inode *ea_inode, goto out; ref_count = ext4_xattr_inode_get_ref(ea_inode); - ref_count += ref_change; + if (check_add_overflow(ref_count,ref_change,&new_count)) { + ext4_error_inode(ea_inode, __func__, __LINE__, 0, + "EA inode %lu ref wraparound: ref_count=%llu ref_change=%d", + ea_inode->i_ino, ref_count, ref_change); + ret = -EFSCORRUPTED; + goto out; + } + else { + if(ref_change > 0 && new_count == 1 && ea_inode->i_nlink) { + ext4_error_inode(ea_inode, __func__, __LINE__, 0, + "EA inode %lu i_nlink=%u", + ea_inode->i_ino, ea_inode->i_nlink); + ret = -EFSCORRUPTED; + goto out; + } + else if(ref_change < 0 && new_count == 0 && ea_inode->i_nlink != 1) { + ext4_error_inode(ea_inode, __func__, __LINE__, 0, + "EA inode %lu i_nlink=%u", + ea_inode->i_ino, ea_inode->i_nlink); + ret = -EFSCORRUPTED; + goto out; + } + } + ref_count = new_count; ext4_xattr_inode_set_ref(ea_inode, ref_count); if (ref_change > 0) { - WARN_ONCE(ref_count <= 0, "EA inode %lu ref_count=%lld", - ea_inode->i_ino, ref_count); - if (ref_count == 1) { - WARN_ONCE(ea_inode->i_nlink, "EA inode %lu i_nlink=%u", - ea_inode->i_ino, ea_inode->i_nlink); - set_nlink(ea_inode, 1); ext4_orphan_del(handle, ea_inode); } } else { - WARN_ONCE(ref_count < 0, "EA inode %lu ref_count=%lld", - ea_inode->i_ino, ref_count); - if (ref_count == 0) { - WARN_ONCE(ea_inode->i_nlink != 1, - "EA inode %lu i_nlink=%u", - ea_inode->i_ino, ea_inode->i_nlink); - clear_nlink(ea_inode); ext4_orphan_add(handle, ea_inode); }