Skip to content

Commit 90f8572

Browse files
committed
vfs: Commit to never having exectuables on proc and sysfs.
Today proc and sysfs do not contain any executable files. Several applications today mount proc or sysfs without noexec and nosuid and then depend on there being no exectuables files on proc or sysfs. Having any executable files show on proc or sysfs would cause a user space visible regression, and most likely security problems. Therefore commit to never allowing executables on proc and sysfs by adding a new flag to mark them as filesystems without executables and enforce that flag. Test the flag where MNT_NOEXEC is tested today, so that the only user visible effect will be that exectuables will be treated as if the execute bit is cleared. The filesystems proc and sysfs do not currently incoporate any executable files so this does not result in any user visible effects. This makes it unnecessary to vet changes to proc and sysfs tightly for adding exectuable files or changes to chattr that would modify existing files, as no matter what the individual file say they will not be treated as exectuable files by the vfs. Not having to vet changes to closely is important as without this we are only one proc_create call (or another goof up in the implementation of notify_change) from having problematic executables on proc. Those mistakes are all too easy to make and would create a situation where there are security issues or the assumptions of some program having to be broken (and cause userspace regressions). Signed-off-by: "Eric W. Biederman" <[email protected]>
1 parent d770e55 commit 90f8572

File tree

9 files changed

+23
-9
lines changed

9 files changed

+23
-9
lines changed

fs/exec.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,12 @@ static inline void put_binfmt(struct linux_binfmt * fmt)
9898
module_put(fmt->module);
9999
}
100100

101+
bool path_noexec(const struct path *path)
102+
{
103+
return (path->mnt->mnt_flags & MNT_NOEXEC) ||
104+
(path->mnt->mnt_sb->s_iflags & SB_I_NOEXEC);
105+
}
106+
101107
#ifdef CONFIG_USELIB
102108
/*
103109
* Note that a shared library must be both readable and executable due to
@@ -132,7 +138,7 @@ SYSCALL_DEFINE1(uselib, const char __user *, library)
132138
goto exit;
133139

134140
error = -EACCES;
135-
if (file->f_path.mnt->mnt_flags & MNT_NOEXEC)
141+
if (path_noexec(&file->f_path))
136142
goto exit;
137143

138144
fsnotify_open(file);
@@ -777,7 +783,7 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)
777783
if (!S_ISREG(file_inode(file)->i_mode))
778784
goto exit;
779785

780-
if (file->f_path.mnt->mnt_flags & MNT_NOEXEC)
786+
if (path_noexec(&file->f_path))
781787
goto exit;
782788

783789
err = deny_write_access(file);

fs/open.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
377377
* with the "noexec" flag.
378378
*/
379379
res = -EACCES;
380-
if (path.mnt->mnt_flags & MNT_NOEXEC)
380+
if (path_noexec(&path))
381381
goto out_path_release;
382382
}
383383

fs/proc/root.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ static struct dentry *proc_mount(struct file_system_type *fs_type,
134134
}
135135

136136
sb->s_flags |= MS_ACTIVE;
137+
/* User space would break if executables appear on proc */
138+
sb->s_iflags |= SB_I_NOEXEC;
137139
}
138140

139141
return dget(sb->s_root);

fs/sysfs/mount.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type,
4040
SYSFS_MAGIC, &new_sb, ns);
4141
if (IS_ERR(root) || !new_sb)
4242
kobj_ns_drop(KOBJ_NS_TYPE_NET, ns);
43+
else if (new_sb)
44+
/* Userspace would break if executables appear on sysfs */
45+
root->d_sb->s_iflags |= SB_I_NOEXEC;
46+
4347
return root;
4448
}
4549

include/linux/fs.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,6 +1244,7 @@ struct mm_struct;
12441244

12451245
/* sb->s_iflags */
12461246
#define SB_I_CGROUPWB 0x00000001 /* cgroup-aware writeback enabled */
1247+
#define SB_I_NOEXEC 0x00000002 /* Ignore executables on this fs */
12471248

12481249
/* Possible states of 'frozen' field */
12491250
enum {
@@ -3030,4 +3031,6 @@ static inline bool dir_relax(struct inode *inode)
30303031
return !IS_DEADDIR(inode);
30313032
}
30323033

3034+
extern bool path_noexec(const struct path *path);
3035+
30333036
#endif /* _LINUX_FS_H */

kernel/sys.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1668,8 +1668,7 @@ static int prctl_set_mm_exe_file(struct mm_struct *mm, unsigned int fd)
16681668
* overall picture.
16691669
*/
16701670
err = -EACCES;
1671-
if (!S_ISREG(inode->i_mode) ||
1672-
exe.file->f_path.mnt->mnt_flags & MNT_NOEXEC)
1671+
if (!S_ISREG(inode->i_mode) || path_noexec(&exe.file->f_path))
16731672
goto exit;
16741673

16751674
err = inode_permission(inode, MAY_EXEC);

mm/mmap.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1268,7 +1268,7 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
12681268
* mounted, in which case we dont add PROT_EXEC.)
12691269
*/
12701270
if ((prot & PROT_READ) && (current->personality & READ_IMPLIES_EXEC))
1271-
if (!(file && (file->f_path.mnt->mnt_flags & MNT_NOEXEC)))
1271+
if (!(file && path_noexec(&file->f_path)))
12721272
prot |= PROT_EXEC;
12731273

12741274
if (!(flags & MAP_FIXED))
@@ -1337,7 +1337,7 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
13371337
case MAP_PRIVATE:
13381338
if (!(file->f_mode & FMODE_READ))
13391339
return -EACCES;
1340-
if (file->f_path.mnt->mnt_flags & MNT_NOEXEC) {
1340+
if (path_noexec(&file->f_path)) {
13411341
if (vm_flags & VM_EXEC)
13421342
return -EPERM;
13431343
vm_flags &= ~VM_MAYEXEC;

mm/nommu.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1035,7 +1035,7 @@ static int validate_mmap_request(struct file *file,
10351035

10361036
/* handle executable mappings and implied executable
10371037
* mappings */
1038-
if (file->f_path.mnt->mnt_flags & MNT_NOEXEC) {
1038+
if (path_noexec(&file->f_path)) {
10391039
if (prot & PROT_EXEC)
10401040
return -EPERM;
10411041
} else if ((prot & PROT_READ) && !(prot & PROT_EXEC)) {

security/security.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -776,7 +776,7 @@ static inline unsigned long mmap_prot(struct file *file, unsigned long prot)
776776
* ditto if it's not on noexec mount, except that on !MMU we need
777777
* NOMMU_MAP_EXEC (== VM_MAYEXEC) in this case
778778
*/
779-
if (!(file->f_path.mnt->mnt_flags & MNT_NOEXEC)) {
779+
if (!path_noexec(&file->f_path)) {
780780
#ifndef CONFIG_MMU
781781
if (file->f_op->mmap_capabilities) {
782782
unsigned caps = file->f_op->mmap_capabilities(file);

0 commit comments

Comments
 (0)