aboutsummaryrefslogtreecommitdiffstats
path: root/fs/exec.c
diff options
context:
space:
mode:
authorJann Horn <jann@thejh.net>2015-04-19 02:48:39 +0200
committerSimon Shields <keepcalm444@gmail.com>2016-03-15 18:19:42 +1100
commitceb67cdf816dafbd03a31821eaaf718cb180c414 (patch)
tree6239a0749bd9a2f564f8d1c2805763a0164af969 /fs/exec.c
parentb6e29030f9325ccc841ad02e4687dedf6d9bba08 (diff)
downloadkernel_samsung_smdk4412-ceb67cdf816dafbd03a31821eaaf718cb180c414.tar.gz
kernel_samsung_smdk4412-ceb67cdf816dafbd03a31821eaaf718cb180c414.tar.bz2
kernel_samsung_smdk4412-ceb67cdf816dafbd03a31821eaaf718cb180c414.zip
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> Conflicts: fs/exec.c Change-Id: Iecebf23d07e299689e4ba4fd74ea8821ef96e72b
Diffstat (limited to 'fs/exec.c')
-rw-r--r--fs/exec.c65
1 files changed, 40 insertions, 25 deletions
diff --git a/fs/exec.c b/fs/exec.c
index 9ab31ca17c1..807400fd9f4 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1267,6 +1267,45 @@ int check_unsafe_exec(struct linux_binprm *bprm)
return res;
}
+static void bprm_fill_uid(struct linux_binprm *bprm)
+{
+ struct inode *inode;
+ unsigned int mode;
+ uid_t uid;
+ gid_t gid;
+
+ /* clear any previous set[ug]id data from a previous binary */
+ bprm->cred->euid = current_euid();
+ bprm->cred->egid = current_egid();
+
+ if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
+ return;
+
+ inode = bprm->file->f_path.dentry->d_inode;
+ mode = ACCESS_ONCE(inode->i_mode);
+ if (!(mode & (S_ISUID|S_ISGID)))
+ return;
+
+ /* Be careful if suid/sgid is set */
+ mutex_lock(&inode->i_mutex);
+
+ /* reload atomically mode/uid/gid now that lock held */
+ mode = inode->i_mode;
+ uid = inode->i_uid;
+ gid = inode->i_gid;
+ mutex_unlock(&inode->i_mutex);
+
+ if (mode & S_ISUID) {
+ bprm->per_clear |= PER_CLEAR_ON_SETID;
+ bprm->cred->euid = uid;
+ }
+
+ if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
+ bprm->per_clear |= PER_CLEAR_ON_SETID;
+ bprm->cred->egid = gid;
+ }
+}
+
/*
* Fill the binprm structure from the inode.
* Check permissions, then read the first 128 (BINPRM_BUF_SIZE) bytes
@@ -1275,36 +1314,12 @@ int check_unsafe_exec(struct linux_binprm *bprm)
*/
int prepare_binprm(struct linux_binprm *bprm)
{
- umode_t mode;
- struct inode * inode = bprm->file->f_path.dentry->d_inode;
int retval;
- mode = inode->i_mode;
if (bprm->file->f_op == NULL)
return -EACCES;
- /* clear any previous set[ug]id data from a previous binary */
- bprm->cred->euid = current_euid();
- bprm->cred->egid = current_egid();
-
- if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)) {
- /* Set-uid? */
- if (mode & S_ISUID) {
- bprm->per_clear |= PER_CLEAR_ON_SETID;
- bprm->cred->euid = inode->i_uid;
- }
-
- /* Set-gid? */
- /*
- * If setgid is set but no group execute bit then this
- * is a candidate for mandatory locking, not a setgid
- * executable.
- */
- if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
- bprm->per_clear |= PER_CLEAR_ON_SETID;
- bprm->cred->egid = inode->i_gid;
- }
- }
+ bprm_fill_uid(bprm);
/* fill in binprm security blob */
retval = security_bprm_set_creds(bprm);