Skip to content

Commit ad5e014

Browse files
thejhHashcode
authored andcommitted
fs: take i_mutex during prepare_binprm for set[ug]id executables
commit 8b01fc86b9f425899f8a3a8fc1c47d73c2c20543 upstream. This prevents a race between chown() and execve(), where chowning a setuid-user binary to root would momentarily make the binary setuid root. This patch was mostly written by Linus Torvalds. Signed-off-by: Jann Horn <jann@thejh.net> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> [lizf: Backported to 3.4: - adjust context - remove task_no_new_priv and user namespace stuff - open-code file_inode() - s/READ_ONCE/ACCESS_ONCE] Signed-off-by: Zefan Li <lizefan@huawei.com>
1 parent 06bbf30 commit ad5e014

1 file changed

Lines changed: 40 additions & 25 deletions

File tree

fs/exec.c

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1227,6 +1227,45 @@ void install_exec_creds(struct linux_binprm *bprm)
12271227
}
12281228
EXPORT_SYMBOL(install_exec_creds);
12291229

1230+
static void bprm_fill_uid(struct linux_binprm *bprm)
1231+
{
1232+
struct inode *inode;
1233+
unsigned int mode;
1234+
uid_t uid;
1235+
gid_t gid;
1236+
1237+
/* clear any previous set[ug]id data from a previous binary */
1238+
bprm->cred->euid = current_euid();
1239+
bprm->cred->egid = current_egid();
1240+
1241+
if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
1242+
return;
1243+
1244+
inode = bprm->file->f_path.dentry->d_inode;
1245+
mode = ACCESS_ONCE(inode->i_mode);
1246+
if (!(mode & (S_ISUID|S_ISGID)))
1247+
return;
1248+
1249+
/* Be careful if suid/sgid is set */
1250+
mutex_lock(&inode->i_mutex);
1251+
1252+
/* reload atomically mode/uid/gid now that lock held */
1253+
mode = inode->i_mode;
1254+
uid = inode->i_uid;
1255+
gid = inode->i_gid;
1256+
mutex_unlock(&inode->i_mutex);
1257+
1258+
if (mode & S_ISUID) {
1259+
bprm->per_clear |= PER_CLEAR_ON_SETID;
1260+
bprm->cred->euid = uid;
1261+
}
1262+
1263+
if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
1264+
bprm->per_clear |= PER_CLEAR_ON_SETID;
1265+
bprm->cred->egid = gid;
1266+
}
1267+
}
1268+
12301269
/*
12311270
* determine how safe it is to execute the proposed program
12321271
* - the caller must hold ->cred_guard_mutex to protect against
@@ -1276,36 +1315,12 @@ static int check_unsafe_exec(struct linux_binprm *bprm)
12761315
*/
12771316
int prepare_binprm(struct linux_binprm *bprm)
12781317
{
1279-
umode_t mode;
1280-
struct inode * inode = bprm->file->f_path.dentry->d_inode;
12811318
int retval;
12821319

1283-
mode = inode->i_mode;
12841320
if (bprm->file->f_op == NULL)
12851321
return -EACCES;
12861322

1287-
/* clear any previous set[ug]id data from a previous binary */
1288-
bprm->cred->euid = current_euid();
1289-
bprm->cred->egid = current_egid();
1290-
1291-
if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)) {
1292-
/* Set-uid? */
1293-
if (mode & S_ISUID) {
1294-
bprm->per_clear |= PER_CLEAR_ON_SETID;
1295-
bprm->cred->euid = inode->i_uid;
1296-
}
1297-
1298-
/* Set-gid? */
1299-
/*
1300-
* If setgid is set but no group execute bit then this
1301-
* is a candidate for mandatory locking, not a setgid
1302-
* executable.
1303-
*/
1304-
if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
1305-
bprm->per_clear |= PER_CLEAR_ON_SETID;
1306-
bprm->cred->egid = inode->i_gid;
1307-
}
1308-
}
1323+
bprm_fill_uid(bprm);
13091324

13101325
/* fill in binprm security blob */
13111326
retval = security_bprm_set_creds(bprm);

0 commit comments

Comments
 (0)