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
This commit is contained in:
@@ -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,
|
|||||||
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()
|
||||||
*/
|
*/
|
||||||
|
11
mm/mmap.c
11
mm/mmap.c
@@ -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,
|
|||||||
*/
|
*/
|
||||||
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,
|
|||||||
* 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,
|
|||||||
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,
|
|||||||
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);
|
|||||||
/* 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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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 = {
|
|||||||
.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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
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;
|
||||||
|
Reference in New Issue
Block a user