Commit ad658cec authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/selinux-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/selinux-2.6:
  VM/Security: add security hook to do_brk
  Security: round mmap hint address above mmap_min_addr
  security: protect from stack expantion into low vm addresses
  Security: allow capable check to permit mmap or low vm space
  SELinux: detect dead booleans
  SELinux: do not clear f_op when removing entries
parents 2a1292b3 5a211a5d
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/prio_tree.h> #include <linux/prio_tree.h>
#include <linux/debug_locks.h> #include <linux/debug_locks.h>
#include <linux/mm_types.h> #include <linux/mm_types.h>
#include <linux/security.h>
struct mempolicy; struct mempolicy;
struct anon_vma; struct anon_vma;
...@@ -512,6 +513,21 @@ static inline void set_page_links(struct page *page, enum zone_type zone, ...@@ -512,6 +513,21 @@ static inline void set_page_links(struct page *page, enum zone_type zone,
set_page_section(page, pfn_to_section_nr(pfn)); 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)
{
#ifdef CONFIG_SECURITY
hint &= PAGE_MASK;
if (((void *)hint != NULL) &&
(hint < mmap_min_addr))
return PAGE_ALIGN(mmap_min_addr);
#endif
return hint;
}
/* /*
* Some inline functions in vmstat.h depend on page_zone() * Some inline functions in vmstat.h depend on page_zone()
*/ */
......
...@@ -912,6 +912,9 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr, ...@@ -912,6 +912,9 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr,
if (!len) if (!len)
return -EINVAL; return -EINVAL;
if (!(flags & MAP_FIXED))
addr = round_hint_to_min(addr);
error = arch_mmap_check(addr, len, flags); error = arch_mmap_check(addr, len, flags);
if (error) if (error)
return error; return error;
...@@ -1615,6 +1618,12 @@ static inline int expand_downwards(struct vm_area_struct *vma, ...@@ -1615,6 +1618,12 @@ static inline int expand_downwards(struct vm_area_struct *vma,
*/ */
if (unlikely(anon_vma_prepare(vma))) if (unlikely(anon_vma_prepare(vma)))
return -ENOMEM; return -ENOMEM;
address &= PAGE_MASK;
error = security_file_mmap(0, 0, 0, 0, address, 1);
if (error)
return error;
anon_vma_lock(vma); anon_vma_lock(vma);
/* /*
...@@ -1622,8 +1631,6 @@ static inline int expand_downwards(struct vm_area_struct *vma, ...@@ -1622,8 +1631,6 @@ static inline int expand_downwards(struct vm_area_struct *vma,
* is required to hold the mmap_sem in read mode. We need the * is required to hold the mmap_sem in read mode. We need the
* anon_vma lock to serialize against concurrent expand_stacks. * anon_vma lock to serialize against concurrent expand_stacks.
*/ */
address &= PAGE_MASK;
error = 0;
/* Somebody else might have raced and expanded it already */ /* Somebody else might have raced and expanded it already */
if (address < vma->vm_start) { if (address < vma->vm_start) {
......
...@@ -829,6 +829,9 @@ unsigned long do_mmap_pgoff(struct file *file, ...@@ -829,6 +829,9 @@ unsigned long do_mmap_pgoff(struct file *file,
void *result; void *result;
int ret; int ret;
if (!(flags & MAP_FIXED))
addr = round_hint_to_min(addr);
/* decide whether we should attempt the mapping, and if so what sort of /* decide whether we should attempt the mapping, and if so what sort of
* mapping */ * mapping */
ret = validate_mmap_request(file, addr, len, prot, flags, pgoff, ret = validate_mmap_request(file, addr, len, prot, flags, pgoff,
......
...@@ -426,7 +426,7 @@ static int dummy_file_mmap (struct file *file, unsigned long reqprot, ...@@ -426,7 +426,7 @@ static int dummy_file_mmap (struct file *file, unsigned long reqprot,
unsigned long addr, unsigned long addr,
unsigned long addr_only) unsigned long addr_only)
{ {
if (addr < mmap_min_addr) if ((addr < mmap_min_addr) && !capable(CAP_SYS_RAWIO))
return -EACCES; return -EACCES;
return 0; return 0;
} }
......
...@@ -65,6 +65,7 @@ static DEFINE_MUTEX(sel_mutex); ...@@ -65,6 +65,7 @@ static DEFINE_MUTEX(sel_mutex);
/* global data for booleans */ /* global data for booleans */
static struct dentry *bool_dir = NULL; static struct dentry *bool_dir = NULL;
static int bool_num = 0; static int bool_num = 0;
static char **bool_pending_names;
static int *bool_pending_values = NULL; static int *bool_pending_values = NULL;
/* global data for classes */ /* global data for classes */
...@@ -832,15 +833,16 @@ static ssize_t sel_read_bool(struct file *filep, char __user *buf, ...@@ -832,15 +833,16 @@ static ssize_t sel_read_bool(struct file *filep, char __user *buf,
ssize_t length; ssize_t length;
ssize_t ret; ssize_t ret;
int cur_enforcing; int cur_enforcing;
struct inode *inode; struct inode *inode = filep->f_path.dentry->d_inode;
unsigned index = inode->i_ino & SEL_INO_MASK;
const char *name = filep->f_path.dentry->d_name.name;
mutex_lock(&sel_mutex); mutex_lock(&sel_mutex);
ret = -EFAULT; if (index >= bool_num || strcmp(name, bool_pending_names[index])) {
ret = -EINVAL;
/* check to see if this file has been deleted */
if (!filep->f_op)
goto out; goto out;
}
if (count > PAGE_SIZE) { if (count > PAGE_SIZE) {
ret = -EINVAL; ret = -EINVAL;
...@@ -851,15 +853,13 @@ static ssize_t sel_read_bool(struct file *filep, char __user *buf, ...@@ -851,15 +853,13 @@ static ssize_t sel_read_bool(struct file *filep, char __user *buf,
goto out; goto out;
} }
inode = filep->f_path.dentry->d_inode; cur_enforcing = security_get_bool_value(index);
cur_enforcing = security_get_bool_value(inode->i_ino&SEL_INO_MASK);
if (cur_enforcing < 0) { if (cur_enforcing < 0) {
ret = cur_enforcing; ret = cur_enforcing;
goto out; goto out;
} }
length = scnprintf(page, PAGE_SIZE, "%d %d", cur_enforcing, length = scnprintf(page, PAGE_SIZE, "%d %d", cur_enforcing,
bool_pending_values[inode->i_ino&SEL_INO_MASK]); bool_pending_values[index]);
ret = simple_read_from_buffer(buf, count, ppos, page, length); ret = simple_read_from_buffer(buf, count, ppos, page, length);
out: out:
mutex_unlock(&sel_mutex); mutex_unlock(&sel_mutex);
...@@ -872,9 +872,11 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf, ...@@ -872,9 +872,11 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
char *page = NULL; char *page = NULL;
ssize_t length = -EFAULT; ssize_t length;
int new_value; int new_value;
struct inode *inode; struct inode *inode = filep->f_path.dentry->d_inode;
unsigned index = inode->i_ino & SEL_INO_MASK;
const char *name = filep->f_path.dentry->d_name.name;
mutex_lock(&sel_mutex); mutex_lock(&sel_mutex);
...@@ -882,16 +884,19 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf, ...@@ -882,16 +884,19 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
if (length) if (length)
goto out; goto out;
/* check to see if this file has been deleted */ if (index >= bool_num || strcmp(name, bool_pending_names[index])) {
if (!filep->f_op) length = -EINVAL;
goto out; goto out;
}
if (count >= PAGE_SIZE) { if (count >= PAGE_SIZE) {
length = -ENOMEM; length = -ENOMEM;
goto out; goto out;
} }
if (*ppos != 0) { if (*ppos != 0) {
/* No partial writes. */ /* No partial writes. */
length = -EINVAL;
goto out; goto out;
} }
page = (char*)get_zeroed_page(GFP_KERNEL); page = (char*)get_zeroed_page(GFP_KERNEL);
...@@ -900,6 +905,7 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf, ...@@ -900,6 +905,7 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
goto out; goto out;
} }
length = -EFAULT;
if (copy_from_user(page, buf, count)) if (copy_from_user(page, buf, count))
goto out; goto out;
...@@ -910,8 +916,7 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf, ...@@ -910,8 +916,7 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
if (new_value) if (new_value)
new_value = 1; new_value = 1;
inode = filep->f_path.dentry->d_inode; bool_pending_values[index] = new_value;
bool_pending_values[inode->i_ino&SEL_INO_MASK] = new_value;
length = count; length = count;
out: out:
...@@ -931,7 +936,7 @@ static ssize_t sel_commit_bools_write(struct file *filep, ...@@ -931,7 +936,7 @@ static ssize_t sel_commit_bools_write(struct file *filep,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
char *page = NULL; char *page = NULL;
ssize_t length = -EFAULT; ssize_t length;
int new_value; int new_value;
mutex_lock(&sel_mutex); mutex_lock(&sel_mutex);
...@@ -940,10 +945,6 @@ static ssize_t sel_commit_bools_write(struct file *filep, ...@@ -940,10 +945,6 @@ static ssize_t sel_commit_bools_write(struct file *filep,
if (length) if (length)
goto out; goto out;
/* check to see if this file has been deleted */
if (!filep->f_op)
goto out;
if (count >= PAGE_SIZE) { if (count >= PAGE_SIZE) {
length = -ENOMEM; length = -ENOMEM;
goto out; goto out;
...@@ -958,6 +959,7 @@ static ssize_t sel_commit_bools_write(struct file *filep, ...@@ -958,6 +959,7 @@ static ssize_t sel_commit_bools_write(struct file *filep,
goto out; goto out;
} }
length = -EFAULT;
if (copy_from_user(page, buf, count)) if (copy_from_user(page, buf, count))
goto out; goto out;
...@@ -982,11 +984,9 @@ static const struct file_operations sel_commit_bools_ops = { ...@@ -982,11 +984,9 @@ static const struct file_operations sel_commit_bools_ops = {
.write = sel_commit_bools_write, .write = sel_commit_bools_write,
}; };
/* partial revoke() from fs/proc/generic.c proc_kill_inodes */
static void sel_remove_entries(struct dentry *de) static void sel_remove_entries(struct dentry *de)
{ {
struct list_head *p, *node; struct list_head *node;
struct super_block *sb = de->d_sb;
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
node = de->d_subdirs.next; node = de->d_subdirs.next;
...@@ -1006,18 +1006,6 @@ static void sel_remove_entries(struct dentry *de) ...@@ -1006,18 +1006,6 @@ static void sel_remove_entries(struct dentry *de)
} }
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
file_list_lock();
list_for_each(p, &sb->s_files) {
struct file * filp = list_entry(p, struct file, f_u.fu_list);
struct dentry * dentry = filp->f_path.dentry;
if (dentry->d_parent != de) {
continue;
}
filp->f_op = NULL;
}
file_list_unlock();
} }
#define BOOL_DIR_NAME "booleans" #define BOOL_DIR_NAME "booleans"
...@@ -1036,7 +1024,9 @@ static int sel_make_bools(void) ...@@ -1036,7 +1024,9 @@ static int sel_make_bools(void)
u32 sid; u32 sid;
/* remove any existing files */ /* remove any existing files */
kfree(bool_pending_names);
kfree(bool_pending_values); kfree(bool_pending_values);
bool_pending_names = NULL;
bool_pending_values = NULL; bool_pending_values = NULL;
sel_remove_entries(dir); sel_remove_entries(dir);
...@@ -1078,16 +1068,17 @@ static int sel_make_bools(void) ...@@ -1078,16 +1068,17 @@ static int sel_make_bools(void)
d_add(dentry, inode); d_add(dentry, inode);
} }
bool_num = num; bool_num = num;
bool_pending_names = names;
bool_pending_values = values; bool_pending_values = values;
out: out:
free_page((unsigned long)page); free_page((unsigned long)page);
return ret;
err:
if (names) { if (names) {
for (i = 0; i < num; i++) for (i = 0; i < num; i++)
kfree(names[i]); kfree(names[i]);
kfree(names); kfree(names);
} }
return ret;
err:
kfree(values); kfree(values);
sel_remove_entries(dir); sel_remove_entries(dir);
ret = -ENOMEM; ret = -ENOMEM;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment