Skip to content

Commit

Permalink
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel…
Browse files Browse the repository at this point in the history
…/git/jmorris/security-testing-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6:
  security: define round_hint_to_min in !CONFIG_SECURITY
  Security/SELinux: seperate lsm specific mmap_min_addr
  SELinux: call cap_file_mmap in selinux_file_mmap
  Capabilities: move cap_file_mmap to commoncap.c
  • Loading branch information
torvalds committed Aug 17, 2009
2 parents 08e53fc + 1d99597 commit 52dec22
Show file tree
Hide file tree
Showing 12 changed files with 137 additions and 41 deletions.
15 changes: 0 additions & 15 deletions include/linux/mm.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ extern int sysctl_legacy_va_layout;
#define sysctl_legacy_va_layout 0
#endif

extern unsigned long mmap_min_addr;

#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
Expand Down Expand Up @@ -574,19 +572,6 @@ static inline void set_page_links(struct page *page, enum zone_type zone,
set_page_section(page, pfn_to_section_nr(pfn));
}

/*
* If a hint addr is less than mmap_min_addr change hint to be as
* low as possible but still greater than mmap_min_addr
*/
static inline unsigned long round_hint_to_min(unsigned long hint)
{
hint &= PAGE_MASK;
if (((void *)hint != NULL) &&
(hint < mmap_min_addr))
return PAGE_ALIGN(mmap_min_addr);
return hint;
}

/*
* Some inline functions in vmstat.h depend on page_zone()
*/
Expand Down
24 changes: 21 additions & 3 deletions include/linux/security.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <linux/resource.h>
#include <linux/sem.h>
#include <linux/shm.h>
#include <linux/mm.h> /* PAGE_ALIGN */
#include <linux/msg.h>
#include <linux/sched.h>
#include <linux/key.h>
Expand Down Expand Up @@ -66,6 +67,9 @@ extern int cap_inode_setxattr(struct dentry *dentry, const char *name,
extern int cap_inode_removexattr(struct dentry *dentry, const char *name);
extern int cap_inode_need_killpriv(struct dentry *dentry);
extern int cap_inode_killpriv(struct dentry *dentry);
extern int cap_file_mmap(struct file *file, unsigned long reqprot,
unsigned long prot, unsigned long flags,
unsigned long addr, unsigned long addr_only);
extern int cap_task_fix_setuid(struct cred *new, const struct cred *old, int flags);
extern int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5);
Expand All @@ -92,6 +96,7 @@ extern int cap_netlink_send(struct sock *sk, struct sk_buff *skb);
extern int cap_netlink_recv(struct sk_buff *skb, int cap);

extern unsigned long mmap_min_addr;
extern unsigned long dac_mmap_min_addr;
/*
* Values used in the task_security_ops calls
*/
Expand All @@ -116,6 +121,21 @@ struct request_sock;
#define LSM_UNSAFE_PTRACE 2
#define LSM_UNSAFE_PTRACE_CAP 4

/*
* If a hint addr is less than mmap_min_addr change hint to be as
* low as possible but still greater than mmap_min_addr
*/
static inline unsigned long round_hint_to_min(unsigned long hint)
{
hint &= PAGE_MASK;
if (((void *)hint != NULL) &&
(hint < mmap_min_addr))
return PAGE_ALIGN(mmap_min_addr);
return hint;
}
extern int mmap_min_addr_handler(struct ctl_table *table, int write, struct file *filp,
void __user *buffer, size_t *lenp, loff_t *ppos);

#ifdef CONFIG_SECURITY

struct security_mnt_opts {
Expand Down Expand Up @@ -2197,9 +2217,7 @@ static inline int security_file_mmap(struct file *file, unsigned long reqprot,
unsigned long addr,
unsigned long addr_only)
{
if ((addr < mmap_min_addr) && !capable(CAP_SYS_RAWIO))
return -EACCES;
return 0;
return cap_file_mmap(file, reqprot, prot, flags, addr, addr_only);
}

static inline int security_file_mprotect(struct vm_area_struct *vma,
Expand Down
7 changes: 4 additions & 3 deletions kernel/sysctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#include <linux/acpi.h>
#include <linux/reboot.h>
#include <linux/ftrace.h>
#include <linux/security.h>
#include <linux/slow-work.h>
#include <linux/perf_counter.h>

Expand Down Expand Up @@ -1306,10 +1307,10 @@ static struct ctl_table vm_table[] = {
{
.ctl_name = CTL_UNNUMBERED,
.procname = "mmap_min_addr",
.data = &mmap_min_addr,
.maxlen = sizeof(unsigned long),
.data = &dac_mmap_min_addr,
.maxlen = sizeof(unsigned long),
.mode = 0644,
.proc_handler = &proc_doulongvec_minmax,
.proc_handler = &mmap_min_addr_handler,
},
#ifdef CONFIG_NUMA
{
Expand Down
6 changes: 3 additions & 3 deletions mm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -225,9 +225,9 @@ config DEFAULT_MMAP_MIN_ADDR
For most ia64, ppc64 and x86 users with lots of address space
a value of 65536 is reasonable and should cause no problems.
On arm and other archs it should not be higher than 32768.
Programs which use vm86 functionality would either need additional
permissions from either the LSM or the capabilities module or have
this protection disabled.
Programs which use vm86 functionality or have some need to map
this low address space will need CAP_SYS_RAWIO or disable this
protection by setting the value to 0.

This value can be changed after boot using the
/proc/sys/vm/mmap_min_addr tunable.
Expand Down
3 changes: 0 additions & 3 deletions mm/mmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,6 @@ int sysctl_overcommit_ratio = 50; /* default is 50% */
int sysctl_max_map_count __read_mostly = DEFAULT_MAX_MAP_COUNT;
struct percpu_counter vm_committed_as;

/* amount of vm to protect from userspace access */
unsigned long mmap_min_addr = CONFIG_DEFAULT_MMAP_MIN_ADDR;

/*
* Check that a process has enough memory to allocate a new virtual
* mapping. 0 means there is enough memory for the allocation to
Expand Down
3 changes: 0 additions & 3 deletions mm/nommu.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,6 @@ int sysctl_max_map_count = DEFAULT_MAX_MAP_COUNT;
int sysctl_nr_trim_pages = CONFIG_NOMMU_INITIAL_TRIM_EXCESS;
int heap_stack_gap = 0;

/* amount of vm to protect from userspace access */
unsigned long mmap_min_addr = CONFIG_DEFAULT_MMAP_MIN_ADDR;

atomic_long_t mmap_pages_allocated;

EXPORT_SYMBOL(mem_map);
Expand Down
16 changes: 16 additions & 0 deletions security/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,22 @@ config SECURITY_ROOTPLUG

If you are unsure how to answer this question, answer N.

config LSM_MMAP_MIN_ADDR
int "Low address space for LSM to from user allocation"
depends on SECURITY && SECURITY_SELINUX
default 65535
help
This is the portion of low virtual memory which should be protected
from userspace allocation. Keeping a user from writing to low pages
can help reduce the impact of kernel NULL pointer bugs.

For most ia64, ppc64 and x86 users with lots of address space
a value of 65536 is reasonable and should cause no problems.
On arm and other archs it should not be higher than 32768.
Programs which use vm86 functionality or have some need to map
this low address space will need the permission specific to the
systems running LSM.

source security/selinux/Kconfig
source security/smack/Kconfig
source security/tomoyo/Kconfig
Expand Down
2 changes: 1 addition & 1 deletion security/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ subdir-$(CONFIG_SECURITY_SMACK) += smack
subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo

# always enable default capabilities
obj-y += commoncap.o
obj-y += commoncap.o min_addr.o

# Object file lists
obj-$(CONFIG_SECURITY) += security.o capability.o
Expand Down
9 changes: 0 additions & 9 deletions security/capability.c
Original file line number Diff line number Diff line change
Expand Up @@ -330,15 +330,6 @@ static int cap_file_ioctl(struct file *file, unsigned int command,
return 0;
}

static int cap_file_mmap(struct file *file, unsigned long reqprot,
unsigned long prot, unsigned long flags,
unsigned long addr, unsigned long addr_only)
{
if ((addr < mmap_min_addr) && !capable(CAP_SYS_RAWIO))
return -EACCES;
return 0;
}

static int cap_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
unsigned long prot)
{
Expand Down
30 changes: 30 additions & 0 deletions security/commoncap.c
Original file line number Diff line number Diff line change
Expand Up @@ -984,3 +984,33 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages)
cap_sys_admin = 1;
return __vm_enough_memory(mm, pages, cap_sys_admin);
}

/*
* cap_file_mmap - check if able to map given addr
* @file: unused
* @reqprot: unused
* @prot: unused
* @flags: unused
* @addr: address attempting to be mapped
* @addr_only: unused
*
* If the process is attempting to map memory below mmap_min_addr they need
* CAP_SYS_RAWIO. The other parameters to this function are unused by the
* capability security module. Returns 0 if this mapping should be allowed
* -EPERM if not.
*/
int cap_file_mmap(struct file *file, unsigned long reqprot,
unsigned long prot, unsigned long flags,
unsigned long addr, unsigned long addr_only)
{
int ret = 0;

if (addr < dac_mmap_min_addr) {
ret = cap_capable(current, current_cred(), CAP_SYS_RAWIO,
SECURITY_CAP_AUDIT);
/* set PF_SUPERPRIV if it turns out we allow the low mmap */
if (ret == 0)
current->flags |= PF_SUPERPRIV;
}
return ret;
}
49 changes: 49 additions & 0 deletions security/min_addr.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/security.h>
#include <linux/sysctl.h>

/* amount of vm to protect from userspace access by both DAC and the LSM*/
unsigned long mmap_min_addr;
/* amount of vm to protect from userspace using CAP_SYS_RAWIO (DAC) */
unsigned long dac_mmap_min_addr = CONFIG_DEFAULT_MMAP_MIN_ADDR;
/* amount of vm to protect from userspace using the LSM = CONFIG_LSM_MMAP_MIN_ADDR */

/*
* Update mmap_min_addr = max(dac_mmap_min_addr, CONFIG_LSM_MMAP_MIN_ADDR)
*/
static void update_mmap_min_addr(void)
{
#ifdef CONFIG_LSM_MMAP_MIN_ADDR
if (dac_mmap_min_addr > CONFIG_LSM_MMAP_MIN_ADDR)
mmap_min_addr = dac_mmap_min_addr;
else
mmap_min_addr = CONFIG_LSM_MMAP_MIN_ADDR;
#else
mmap_min_addr = dac_mmap_min_addr;
#endif
}

/*
* sysctl handler which just sets dac_mmap_min_addr = the new value and then
* calls update_mmap_min_addr() so non MAP_FIXED hints get rounded properly
*/
int mmap_min_addr_handler(struct ctl_table *table, int write, struct file *filp,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
int ret;

ret = proc_doulongvec_minmax(table, write, filp, buffer, lenp, ppos);

update_mmap_min_addr();

return ret;
}

int __init init_mmap_min_addr(void)
{
update_mmap_min_addr();

return 0;
}
pure_initcall(init_mmap_min_addr);
14 changes: 13 additions & 1 deletion security/selinux/hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -3030,9 +3030,21 @@ static int selinux_file_mmap(struct file *file, unsigned long reqprot,
int rc = 0;
u32 sid = current_sid();

if (addr < mmap_min_addr)
/*
* notice that we are intentionally putting the SELinux check before
* the secondary cap_file_mmap check. This is such a likely attempt
* at bad behaviour/exploit that we always want to get the AVC, even
* if DAC would have also denied the operation.
*/
if (addr < CONFIG_LSM_MMAP_MIN_ADDR) {
rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT,
MEMPROTECT__MMAP_ZERO, NULL);
if (rc)
return rc;
}

/* do DAC check on address space usage */
rc = cap_file_mmap(file, reqprot, prot, flags, addr, addr_only);
if (rc || addr_only)
return rc;

Expand Down

0 comments on commit 52dec22

Please sign in to comment.