aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorIngo Molnar <mingo@elte.hu>2004-07-17 19:08:19 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2004-07-17 19:08:19 -0700
commit1bb0fa189c6ae75cbf440244ae77a8ede9912df1 (patch)
tree8f1d4e06906886a4dd5d5705b6bb461957ecf72f /mm
parent4e8688b284f9c714510be1036c245ce6374018b7 (diff)
downloadhistory-1bb0fa189c6ae75cbf440244ae77a8ede9912df1.tar.gz
[PATCH] NX: clean up legacy binary support
This cleans up legacy x86 binary support by introducing a new personality bit: READ_IMPLIES_EXEC, and implements Linus' suggestion to add the PROT_EXEC bit on the two affected syscall entry places, sys_mprotect() and sys_mmap(). If this bit is set then PROT_READ will also add the PROT_EXEC bit - as expected by legacy x86 binaries. The ELF loader will automatically set this bit when it encounters a legacy binary. This approach avoids the problems the previous ->def_flags solution caused. In particular this patch fixes the PROT_NONE problem in a cleaner way (http://lkml.org/lkml/2004/7/12/227), and it should fix the ia64 PROT_EXEC problem reported by David Mosberger. Also, mprotect(PROT_READ) done by legacy binaries will do the right thing as well. the details: - the personality bit is added to the personality mask upon exec(), within the ELF loader, but is not cleared (see the exceptions below). This means that if an environment that already has the bit exec()s a new-style binary it will still get the old behavior. - one exception are setuid/setgid binaries: these will reset the bit - thus local attackers cannot manually set the bit and circumvent NX protection. Legacy setuid binaries will still get the bit through the ELF loader. This gives us maximum flexibility in shaping compatibility environments. - selinux also clears the bit when switching SIDs via exec(). - x86 is the only arch making use of READ_IMPLIES_EXEC currently. Other arches will have the pre-NX-patch protection setup they always had. I have booted an old distro [RH 7.2] and two new PT_GNU_STACK distros [SuSE 9.2 and FC2] on an NX-capable CPU - they work just fine and all the mapping details are right. I've checked the PROT_NONE test-utility as well and it works as expected. I have checked various setuid scenarios as well involving legacy and new-style binaries. an improved setarch utility can be used to set the personality bit manually: http://redhat.com/~mingo/nx-patches/setarch-1.4-3.tar.gz the new '-X' flag does it, e.g.: ./setarch -X linux /bin/cat /proc/self/maps will trigger the old protection layout even on a new distro. Signed-off-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'mm')
-rw-r--r--mm/mmap.c13
-rw-r--r--mm/mprotect.c7
2 files changed, 14 insertions, 6 deletions
diff --git a/mm/mmap.c b/mm/mmap.c
index cd8eec6bdb3ab1..68ea8ab1f72fdf 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -750,6 +750,13 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr,
int accountable = 1;
unsigned long charged = 0;
+ /*
+ * Does the application expect PROT_READ to imply PROT_EXEC:
+ */
+ if (unlikely((prot & PROT_READ) &&
+ (current->personality & READ_IMPLIES_EXEC)))
+ prot |= PROT_EXEC;
+
if (file) {
if (is_file_hugepages(file))
accountable = 0;
@@ -792,12 +799,6 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr,
vm_flags = calc_vm_prot_bits(prot) | calc_vm_flag_bits(flags) |
mm->def_flags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
- /*
- * mm->def_flags might have VM_EXEC set, which PROT_NONE does NOT want.
- */
- if (prot == PROT_NONE)
- vm_flags &= ~VM_EXEC;
-
if (flags & MAP_LOCKED) {
if (!capable(CAP_IPC_LOCK))
return -EPERM;
diff --git a/mm/mprotect.c b/mm/mprotect.c
index 6ce037f650fc5b..88041d46ad0b6b 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -17,6 +17,7 @@
#include <linux/highmem.h>
#include <linux/security.h>
#include <linux/mempolicy.h>
+#include <linux/personality.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
@@ -205,6 +206,12 @@ sys_mprotect(unsigned long start, size_t len, unsigned long prot)
return -EINVAL;
if (end == start)
return 0;
+ /*
+ * Does the application expect PROT_READ to imply PROT_EXEC:
+ */
+ if (unlikely((prot & PROT_READ) &&
+ (current->personality & READ_IMPLIES_EXEC)))
+ prot |= PROT_EXEC;
vm_flags = calc_vm_prot_bits(prot);