This patch works by changing mmap to always set the MAP_DENYWRITE flag
whenever PROT_EXEC is requested. To prevent that from being used as an
annoyance attack by people PROT_EXEC'ing things they don't have execute
permission on, mmap also had to be changed to check for execute permission.
I've been running it for a day or so now, and haven't run into any bad side
effects yet. I did find 2 shared libs on my system that were mode 644 and
should have been 755, so you probably want to check up on your permissions at
least in /lib before booting with this patch.
This is my first non-trivial[1] kernel hack, so your verbose criticism will
be appreciated.
While studying this issue, I found that MAP_DENYWRITE is ignored by mmap when
passed in from outside the kernel, and I assume that's intentional, but it
seems odd that it's done in the arch/* code, and even more odd that
arch/sparc doesn't do it. What's going on there?
[1] Affecting more than one line of code
=== CUT HERE ===
diff -ruN linux/drivers/block/amiflop.c linux.pac/drivers/block/amiflop.c
--- linux/drivers/block/amiflop.c Thu May 16 01:05:10 1996
+++ linux.pac/drivers/block/amiflop.c Thu Aug 20 23:33:08 1998
@@ -1617,7 +1617,7 @@
if (old_dev && old_dev != inode->i_rdev)
invalidate_buffers(old_dev);
- if (filp && filp->f_mode)
+ if (filp && (filp->f_mode & 3))
check_disk_change(inode->i_rdev);
if (filp && (filp->f_flags & (O_WRONLY|O_RDWR))) {
diff -ruN linux/drivers/cdrom/cdrom.c linux.pac/drivers/cdrom/cdrom.c
--- linux/drivers/cdrom/cdrom.c Wed Jun 3 17:17:47 1998
+++ linux.pac/drivers/cdrom/cdrom.c Thu Aug 20 22:38:19 1998
@@ -19,6 +19,7 @@
#include <linux/cdrom.h>
#include <linux/ucdrom.h>
+/* FMODE_WRITE not good enough for you? */
#define FM_WRITE 0x2 /* file mode write bit */
#define VERSION "$Id: cdrom.c,v 0.8 1996/08/10 10:52:11 david Exp $"
diff -ruN linux/fs/exec.c linux.pac/fs/exec.c
--- linux/fs/exec.c Mon Jul 13 15:47:34 1998
+++ linux.pac/fs/exec.c Thu Aug 20 23:32:28 1998
@@ -126,7 +126,7 @@
return -ENFILE;
}
f->f_flags = mode;
- f->f_mode = (mode+1) & O_ACCMODE;
+ f->f_mode = ((mode+1) & O_ACCMODE) | FMODE_EXEC;
f->f_inode = inode;
f->f_pos = 0;
f->f_reada = 0;
diff -ruN linux/fs/fifo.c linux.pac/fs/fifo.c
--- linux/fs/fifo.c Mon Mar 11 04:23:22 1996
+++ linux.pac/fs/fifo.c Thu Aug 20 22:53:02 1998
@@ -15,7 +15,7 @@
int retval = 0;
unsigned long page;
- switch( filp->f_mode ) {
+ switch( filp->f_mode & 3 ) {
case 1:
/*
diff -ruN linux/fs/open.c linux.pac/fs/open.c
--- linux/fs/open.c Sat Nov 30 05:21:19 1996
+++ linux.pac/fs/open.c Thu Aug 20 19:18:09 1998
@@ -522,6 +522,8 @@
if (error)
goto cleanup_inode;
}
+ if (!permission(inode, MAY_EXEC))
+ f->f_mode |= FMODE_EXEC;
f->f_inode = inode;
f->f_pos = 0;
diff -ruN linux/fs/pipe.c linux.pac/fs/pipe.c
--- linux/fs/pipe.c Wed Aug 6 19:52:01 1997
+++ linux.pac/fs/pipe.c Thu Aug 6 19:33:19 1998
@@ -413,7 +413,7 @@
int error;
int i,j;
- error = ENFILE;
+ error = -ENFILE;
f1 = get_empty_filp();
if (!f1)
goto no_files;
diff -ruN linux/include/linux/fs.h linux.pac/include/linux/fs.h
--- linux/include/linux/fs.h Mon Jul 13 15:47:39 1998
+++ linux.pac/include/linux/fs.h Thu Aug 20 18:38:13 1998
@@ -45,6 +45,7 @@
#define FMODE_READ 1
#define FMODE_WRITE 2
+#define FMODE_EXEC 4
#define READ 0
#define WRITE 1
diff -ruN linux/mm/mmap.c linux.pac/mm/mmap.c
--- linux/mm/mmap.c Wed Jun 3 17:17:50 1998
+++ linux.pac/mm/mmap.c Thu Aug 20 23:56:27 1998
@@ -1,3 +1,4 @@
+/* vim:set noet ts=8 sw=8: */
/*
* linux/mm/mmap.c
*
@@ -189,6 +190,27 @@
default:
return -EINVAL;
+ }
+ if (prot & PROT_EXEC) {
+/* There are 2 ways to go on this permission check: set a FMODE_EXEC flag
+ * when the fd is created, or do it now. I prefer the former but it is a bit
+ * more complicated, so I can't be sure I haven't caused any bad side
+ * effects. Switch this #if around to do it the other way.
+ *
+ * The reason I don't like calling permission() from here because it means
+ * that a chmod can revoke permissions from an existing fd, and that doesn't
+ * occur for read and write permissions, so why should it for exec? It does
+ * look like fchdir does it this way, and that should probably be fixed too. */
+#if 1
+ if (!(file->f_mode & FMODE_EXEC))
+ return -EACCES;
+#else
+ int error;
+ error = permission(file->f_inode, MAY_EXEC);
+ if (error)
+ return error;
+#endif
+ flags |= MAP_DENYWRITE;
}
if (flags & MAP_DENYWRITE) {
if (file->f_inode->i_writecount > 0)
=== CUT HERE ===
-- Alan Curry- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.altern.org/andrebalsa/doc/lkml-faq.html