Skip to content

Commit

Permalink
do_last(): fetch directory ->i_mode and ->i_uid before it's too late
Browse files Browse the repository at this point in the history
In do_last of namei.c, there is a possible information disclosure
      due to a double free. This could lead to local information disclosure
      with no additional execution privileges needed. User interaction is
      not needed for exploitation.
      The fix is designed to cache the required data before it is freed.

Change-Id: Ib57b93fafd1742a40157c51e17cc87c30e1a9e53
Signed-off-by: engstk <[email protected]>
  • Loading branch information
binson-xu authored and engstk committed Jul 22, 2020
1 parent 4eaca70 commit 6fb1ca0
Showing 1 changed file with 10 additions and 7 deletions.
17 changes: 10 additions & 7 deletions fs/namei.c
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -1022,7 +1022,8 @@ static int may_linkat(struct path *link)
* may_create_in_sticky - Check whether an O_CREAT open in a sticky directory
* should be allowed, or not, on files that already
* exist.
* @dir: the sticky parent directory
* @dir_mode: mode bits of directory
* @dir_uid: owner of directory
* @inode: the inode of the file to open
*
* Block an O_CREAT open of a FIFO (or a regular file) when:
Expand All @@ -1038,18 +1039,18 @@ static int may_linkat(struct path *link)
*
* Returns 0 if the open is allowed, -ve on error.
*/
static int may_create_in_sticky(struct dentry * const dir,
static int may_create_in_sticky(umode_t dir_mode, kuid_t dir_uid,
struct inode * const inode)
{
if ((!sysctl_protected_fifos && S_ISFIFO(inode->i_mode)) ||
(!sysctl_protected_regular && S_ISREG(inode->i_mode)) ||
likely(!(dir->d_inode->i_mode & S_ISVTX)) ||
uid_eq(inode->i_uid, dir->d_inode->i_uid) ||
likely(!(dir_mode & S_ISVTX)) ||
uid_eq(inode->i_uid, dir_uid) ||
uid_eq(current_fsuid(), inode->i_uid))
return 0;

if (likely(dir->d_inode->i_mode & 0002) ||
(dir->d_inode->i_mode & 0020 &&
if (likely(dir_mode & 0002) ||
(dir_mode & 0020 &&
((sysctl_protected_fifos >= 2 && S_ISFIFO(inode->i_mode)) ||
(sysctl_protected_regular >= 2 && S_ISREG(inode->i_mode))))) {
return -EACCES;
Expand Down Expand Up @@ -3324,6 +3325,8 @@ static int do_last(struct nameidata *nd,
struct file *file, const struct open_flags *op)
{
struct dentry *dir = nd->path.dentry;
kuid_t dir_uid = nd->inode->i_uid;
umode_t dir_mode = nd->inode->i_mode;
int open_flag = op->open_flag;
bool will_truncate = (open_flag & O_TRUNC) != 0;
bool got_write = false;
Expand Down Expand Up @@ -3459,7 +3462,7 @@ static int do_last(struct nameidata *nd,
error = -EISDIR;
if (d_is_dir(nd->path.dentry))
goto out;
error = may_create_in_sticky(dir,
error = may_create_in_sticky(dir_mode, dir_uid,
d_backing_inode(nd->path.dentry));
if (unlikely(error))
goto out;
Expand Down

0 comments on commit 6fb1ca0

Please sign in to comment.