Skip to content

Commit 8b01fc8

Browse files
thejhtorvalds
authored andcommitted
fs: take i_mutex during prepare_binprm for set[ug]id executables
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>
1 parent 5224b96 commit 8b01fc8

File tree

1 file changed

+48
-28
lines changed

1 file changed

+48
-28
lines changed

Diff for: fs/exec.c

+48-28
Original file line numberDiff line numberDiff line change
@@ -1275,6 +1275,53 @@ static void check_unsafe_exec(struct linux_binprm *bprm)
12751275
spin_unlock(&p->fs->lock);
12761276
}
12771277

1278+
static void bprm_fill_uid(struct linux_binprm *bprm)
1279+
{
1280+
struct inode *inode;
1281+
unsigned int mode;
1282+
kuid_t uid;
1283+
kgid_t gid;
1284+
1285+
/* clear any previous set[ug]id data from a previous binary */
1286+
bprm->cred->euid = current_euid();
1287+
bprm->cred->egid = current_egid();
1288+
1289+
if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
1290+
return;
1291+
1292+
if (task_no_new_privs(current))
1293+
return;
1294+
1295+
inode = file_inode(bprm->file);
1296+
mode = READ_ONCE(inode->i_mode);
1297+
if (!(mode & (S_ISUID|S_ISGID)))
1298+
return;
1299+
1300+
/* Be careful if suid/sgid is set */
1301+
mutex_lock(&inode->i_mutex);
1302+
1303+
/* reload atomically mode/uid/gid now that lock held */
1304+
mode = inode->i_mode;
1305+
uid = inode->i_uid;
1306+
gid = inode->i_gid;
1307+
mutex_unlock(&inode->i_mutex);
1308+
1309+
/* We ignore suid/sgid if there are no mappings for them in the ns */
1310+
if (!kuid_has_mapping(bprm->cred->user_ns, uid) ||
1311+
!kgid_has_mapping(bprm->cred->user_ns, gid))
1312+
return;
1313+
1314+
if (mode & S_ISUID) {
1315+
bprm->per_clear |= PER_CLEAR_ON_SETID;
1316+
bprm->cred->euid = uid;
1317+
}
1318+
1319+
if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
1320+
bprm->per_clear |= PER_CLEAR_ON_SETID;
1321+
bprm->cred->egid = gid;
1322+
}
1323+
}
1324+
12781325
/*
12791326
* Fill the binprm structure from the inode.
12801327
* Check permissions, then read the first 128 (BINPRM_BUF_SIZE) bytes
@@ -1283,36 +1330,9 @@ static void check_unsafe_exec(struct linux_binprm *bprm)
12831330
*/
12841331
int prepare_binprm(struct linux_binprm *bprm)
12851332
{
1286-
struct inode *inode = file_inode(bprm->file);
1287-
umode_t mode = inode->i_mode;
12881333
int retval;
12891334

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

13171337
/* fill in binprm security blob */
13181338
retval = security_bprm_set_creds(bprm);

0 commit comments

Comments
 (0)