Commit bc75ac4f authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] Security hook for vm_enough_memory

From: Stephen Smalley <sds@epoch.ncsc.mil>

This patch against 2.5.73 replaces vm_enough_memory with a security hook
per Alan Cox's suggestion so that security modules can completely replace
the logic if desired.

Note that the patch changes the interface to follow the convention of the
other security hooks, i.e.  return 0 if ok or -errno on failure (-ENOMEM in
this case) rather than returning a boolean.  It also exports various
variables and functions required for the vm_enough_memory logic.
parent cee396e2
......@@ -13,6 +13,7 @@
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/security.h>
#include <asm/param.h>
#include <asm/signal.h>
......@@ -177,7 +178,7 @@ ia32_setup_arg_pages (struct linux_binprm *bprm)
if (!mpnt)
return -ENOMEM;
if (!vm_enough_memory((IA32_STACK_TOP - (PAGE_MASK & (unsigned long) bprm->p))>>PAGE_SHIFT)) {
if (security_vm_enough_memory((IA32_STACK_TOP - (PAGE_MASK & (unsigned long) bprm->p))>>PAGE_SHIFT)) {
kmem_cache_free(vm_area_cachep, mpnt);
return -ENOMEM;
}
......
......@@ -100,7 +100,6 @@ ia64_shmat (int shmid, void *shmaddr, int shmflg)
asmlinkage unsigned long
ia64_brk (unsigned long brk)
{
extern int vm_enough_memory (long pages);
unsigned long rlim, retval, newbrk, oldbrk;
struct mm_struct *mm = current->mm;
......
......@@ -28,6 +28,7 @@
#include <linux/vfs.h>
#include <linux/namei.h>
#include <linux/socket.h>
#include <linux/security.h>
#include <asm/ptrace.h>
#include <asm/page.h>
......@@ -527,8 +528,6 @@ asmlinkage int irix_gtime(struct pt_regs *regs)
return get_seconds();
}
int vm_enough_memory(long pages);
/*
* IRIX is completely broken... it returns 0 on success, otherwise
* ENOMEM.
......@@ -585,7 +584,7 @@ asmlinkage int irix_brk(unsigned long brk)
/*
* Check if we have enough memory..
*/
if (!vm_enough_memory((newbrk-oldbrk) >> PAGE_SHIFT)) {
if (security_vm_enough_memory((newbrk-oldbrk) >> PAGE_SHIFT)) {
ret = -ENOMEM;
goto out;
}
......
......@@ -23,6 +23,7 @@
#include <linux/spinlock.h>
#include <linux/binfmts.h>
#include <linux/module.h>
#include <linux/security.h>
#include <asm/uaccess.h>
#include <asm/pgalloc.h>
......@@ -55,7 +56,7 @@ int setup_arg_pages32(struct linux_binprm *bprm)
if (!mpnt)
return -ENOMEM;
if (!vm_enough_memory((STACK_TOP - (PAGE_MASK & (unsigned long) bprm->p))>>PAGE_SHIFT)) {
if (security_vm_enough_memory((STACK_TOP - (PAGE_MASK & (unsigned long) bprm->p))>>PAGE_SHIFT)) {
kmem_cache_free(vm_area_cachep, mpnt);
return -ENOMEM;
}
......
......@@ -14,6 +14,8 @@
#include <linux/string.h>
#include <linux/binfmts.h>
#include <linux/mm.h>
#include <linux/security.h>
#include <asm/segment.h>
#include <asm/ptrace.h>
#include <asm/processor.h>
......@@ -339,7 +341,7 @@ int setup_arg_pages(struct linux_binprm *bprm)
if (!mpnt)
return -ENOMEM;
if (!vm_enough_memory((IA32_STACK_TOP - (PAGE_MASK & (unsigned long) bprm->p))>>PAGE_SHIFT)) {
if (security_vm_enough_memory((IA32_STACK_TOP - (PAGE_MASK & (unsigned long) bprm->p))>>PAGE_SHIFT)) {
kmem_cache_free(vm_area_cachep, mpnt);
return -ENOMEM;
}
......
......@@ -392,7 +392,7 @@ int setup_arg_pages(struct linux_binprm *bprm)
if (!mpnt)
return -ENOMEM;
if (!vm_enough_memory((STACK_TOP - (PAGE_MASK & (unsigned long) bprm->p))>>PAGE_SHIFT)) {
if (security_vm_enough_memory((STACK_TOP - (PAGE_MASK & (unsigned long) bprm->p))>>PAGE_SHIFT)) {
kmem_cache_free(vm_area_cachep, mpnt);
return -ENOMEM;
}
......
......@@ -9,7 +9,8 @@
#define MREMAP_MAYMOVE 1
#define MREMAP_FIXED 2
extern int vm_enough_memory(long pages);
extern int sysctl_overcommit_memory;
extern int sysctl_overcommit_ratio;
extern atomic_t vm_committed_space;
#ifdef CONFIG_SMP
......
......@@ -49,6 +49,7 @@ extern int cap_bprm_secureexec(struct linux_binprm *bprm);
extern int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid, int flags);
extern void cap_task_reparent_to_init (struct task_struct *p);
extern int cap_syslog (int type);
extern int cap_vm_enough_memory (long pages);
static inline int cap_netlink_send (struct sk_buff *skb)
{
......@@ -958,6 +959,10 @@ struct swap_info_struct;
* See the syslog(2) manual page for an explanation of the @type values.
* @type contains the type of action.
* Return 0 if permission is granted.
* @vm_enough_memory:
* Check permissions for allocating a new virtual mapping.
* @pages contains the number of pages.
* Return 0 if permission is granted.
*
* @register_security:
* allow module stacking.
......@@ -989,6 +994,7 @@ struct security_operations {
int (*quotactl) (int cmds, int type, int id, struct super_block * sb);
int (*quota_on) (struct file * f);
int (*syslog) (int type);
int (*vm_enough_memory) (long pages);
int (*bprm_alloc_security) (struct linux_binprm * bprm);
void (*bprm_free_security) (struct linux_binprm * bprm);
......@@ -1238,6 +1244,11 @@ static inline int security_syslog(int type)
return security_ops->syslog(type);
}
static inline int security_vm_enough_memory(long pages)
{
return security_ops->vm_enough_memory(pages);
}
static inline int security_bprm_alloc (struct linux_binprm *bprm)
{
return security_ops->bprm_alloc_security (bprm);
......@@ -1898,6 +1909,11 @@ static inline int security_syslog(int type)
return cap_syslog(type);
}
static inline int security_vm_enough_memory(long pages)
{
return cap_vm_enough_memory(pages);
}
static inline int security_bprm_alloc (struct linux_binprm *bprm)
{
return 0;
......
......@@ -116,6 +116,8 @@ extern kmem_cache_t *bio_cachep;
void ptrinfo(unsigned long addr);
extern atomic_t slab_reclaim_pages;
#endif /* __KERNEL__ */
#endif /* _LINUX_SLAB_H */
......@@ -286,7 +286,7 @@ static inline int dup_mmap(struct mm_struct * mm, struct mm_struct * oldmm)
continue;
if (mpnt->vm_flags & VM_ACCOUNT) {
unsigned int len = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
if (!vm_enough_memory(len))
if (security_vm_enough_memory(len))
goto fail_nomem;
charge += len;
}
......
......@@ -18,6 +18,7 @@
#include <linux/security.h>
#include <linux/hugetlb.h>
#include <linux/profile.h>
#include <linux/module.h>
#include <asm/uaccess.h>
#include <asm/pgalloc.h>
......@@ -53,65 +54,9 @@ int sysctl_overcommit_memory = 0; /* default is heuristic overcommit */
int sysctl_overcommit_ratio = 50; /* default is 50% */
atomic_t vm_committed_space = ATOMIC_INIT(0);
/*
* Check that a process has enough memory to allocate a new virtual
* mapping. 1 means there is enough memory for the allocation to
* succeed and 0 implies there is not.
*
* We currently support three overcommit policies, which are set via the
* vm.overcommit_memory sysctl. See Documentation/vm/overcommit-acounting
*
* Strict overcommit modes added 2002 Feb 26 by Alan Cox.
* Additional code 2002 Jul 20 by Robert Love.
*/
extern atomic_t slab_reclaim_pages;
int vm_enough_memory(long pages)
{
unsigned long free, allowed;
vm_acct_memory(pages);
/*
* Sometimes we want to use more memory than we have
*/
if (sysctl_overcommit_memory == 1)
return 1;
if (sysctl_overcommit_memory == 0) {
free = get_page_cache_size();
free += nr_free_pages();
free += nr_swap_pages;
/*
* Any slabs which are created with the
* SLAB_RECLAIM_ACCOUNT flag claim to have contents
* which are reclaimable, under pressure. The dentry
* cache and most inode caches should fall into this
*/
free += atomic_read(&slab_reclaim_pages);
/*
* Leave the last 3% for root
*/
if (!capable(CAP_SYS_ADMIN))
free -= free / 32;
if (free > pages)
return 1;
vm_unacct_memory(pages);
return 0;
}
allowed = totalram_pages * sysctl_overcommit_ratio / 100;
allowed += total_swap_pages;
if (atomic_read(&vm_committed_space) < allowed)
return 1;
vm_unacct_memory(pages);
return 0;
}
EXPORT_SYMBOL(sysctl_overcommit_memory);
EXPORT_SYMBOL(sysctl_overcommit_ratio);
EXPORT_SYMBOL(vm_committed_space);
/*
* Requires inode->i_mapping->i_shared_sem
......@@ -646,7 +591,7 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr,
* Private writable mapping: check memory availability
*/
charged = len >> PAGE_SHIFT;
if (!vm_enough_memory(charged))
if (security_vm_enough_memory(charged))
return -ENOMEM;
vm_flags |= VM_ACCOUNT;
}
......@@ -950,7 +895,7 @@ int expand_stack(struct vm_area_struct * vma, unsigned long address)
grow = (address - vma->vm_end) >> PAGE_SHIFT;
/* Overcommit.. */
if (!vm_enough_memory(grow)) {
if (security_vm_enough_memory(grow)) {
spin_unlock(&vma->vm_mm->page_table_lock);
return -ENOMEM;
}
......@@ -1004,7 +949,7 @@ int expand_stack(struct vm_area_struct *vma, unsigned long address)
grow = (vma->vm_start - address) >> PAGE_SHIFT;
/* Overcommit.. */
if (!vm_enough_memory(grow)) {
if (security_vm_enough_memory(grow)) {
spin_unlock(&vma->vm_mm->page_table_lock);
return -ENOMEM;
}
......@@ -1376,7 +1321,7 @@ unsigned long do_brk(unsigned long addr, unsigned long len)
if (mm->map_count > MAX_MAP_COUNT)
return -ENOMEM;
if (!vm_enough_memory(len >> PAGE_SHIFT))
if (security_vm_enough_memory(len >> PAGE_SHIFT))
return -ENOMEM;
flags = VM_DATA_DEFAULT_FLAGS | VM_ACCOUNT | mm->def_flags;
......
......@@ -175,7 +175,7 @@ mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev,
if (newflags & VM_WRITE) {
if (!(vma->vm_flags & (VM_ACCOUNT|VM_WRITE|VM_SHARED))) {
charged = (end - start) >> PAGE_SHIFT;
if (!vm_enough_memory(charged))
if (security_vm_enough_memory(charged))
return -ENOMEM;
newflags |= VM_ACCOUNT;
}
......
......@@ -16,6 +16,7 @@
#include <linux/fs.h>
#include <linux/highmem.h>
#include <linux/rmap-locking.h>
#include <linux/security.h>
#include <asm/uaccess.h>
#include <asm/pgalloc.h>
......@@ -385,7 +386,7 @@ unsigned long do_mremap(unsigned long addr,
if (vma->vm_flags & VM_ACCOUNT) {
charged = (new_len - old_len) >> PAGE_SHIFT;
if (!vm_enough_memory(charged))
if (security_vm_enough_memory(charged))
goto out_nc;
}
......
......@@ -43,6 +43,9 @@ int nr_swap_pages;
int numnodes = 1;
int sysctl_lower_zone_protection = 0;
EXPORT_SYMBOL(totalram_pages);
EXPORT_SYMBOL(nr_swap_pages);
/*
* Used by page_zone() to look up the address of the struct zone whose
* id is encoded in the upper bits of page->flags
......@@ -733,6 +736,7 @@ unsigned int nr_free_pages(void)
return sum;
}
EXPORT_SYMBOL(nr_free_pages);
unsigned int nr_used_zone_pages(void)
{
......@@ -825,6 +829,7 @@ DEFINE_PER_CPU(struct page_state, page_states) = {0};
EXPORT_PER_CPU_SYMBOL(page_states);
atomic_t nr_pagecache = ATOMIC_INIT(0);
EXPORT_SYMBOL(nr_pagecache);
#ifdef CONFIG_SMP
DEFINE_PER_CPU(long, nr_pagecache_local) = 0;
#endif
......
......@@ -36,6 +36,7 @@
#include <linux/writeback.h>
#include <linux/vfs.h>
#include <linux/blkdev.h>
#include <linux/security.h>
#include <asm/uaccess.h>
#include <asm/div64.h>
......@@ -507,7 +508,7 @@ static int shmem_notify_change(struct dentry *dentry, struct iattr *attr)
*/
change = VM_ACCT(attr->ia_size) - VM_ACCT(inode->i_size);
if (change > 0) {
if (!vm_enough_memory(change))
if (security_vm_enough_memory(change))
return -ENOMEM;
} else if (attr->ia_size < inode->i_size) {
vm_unacct_memory(-change);
......@@ -1139,7 +1140,7 @@ shmem_file_write(struct file *file, const char __user *buf, size_t count, loff_t
maxpos = inode->i_size;
if (maxpos < pos + count) {
maxpos = pos + count;
if (!vm_enough_memory(VM_ACCT(maxpos) - VM_ACCT(inode->i_size))) {
if (security_vm_enough_memory(VM_ACCT(maxpos) - VM_ACCT(inode->i_size))) {
err = -ENOMEM;
goto out;
}
......@@ -1493,7 +1494,7 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
memcpy(info, symname, len);
inode->i_op = &shmem_symlink_inline_operations;
} else {
if (!vm_enough_memory(VM_ACCT(1))) {
if (security_vm_enough_memory(VM_ACCT(1))) {
iput(inode);
return -ENOMEM;
}
......@@ -1887,7 +1888,7 @@ struct file *shmem_file_setup(char *name, loff_t size, unsigned long flags)
if (size > SHMEM_MAX_BYTES)
return ERR_PTR(-EINVAL);
if ((flags & VM_ACCOUNT) && !vm_enough_memory(VM_ACCT(size)))
if ((flags & VM_ACCOUNT) && security_vm_enough_memory(VM_ACCT(size)))
return ERR_PTR(-ENOMEM);
error = -ENOMEM;
......
......@@ -90,6 +90,7 @@
#include <linux/kallsyms.h>
#include <linux/cpu.h>
#include <linux/sysctl.h>
#include <linux/module.h>
#include <asm/uaccess.h>
#include <asm/cacheflush.h>
......@@ -462,6 +463,7 @@ struct list_head cache_chain;
* SLAB_RECLAIM_ACCOUNT turns this on per-slab
*/
atomic_t slab_reclaim_pages;
EXPORT_SYMBOL(slab_reclaim_pages);
/*
* chicken and egg problem: delay the per-cpu array allocation
......
......@@ -20,6 +20,7 @@
#include <linux/pagemap.h>
#include <linux/pagevec.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mm_inline.h>
#include <linux/buffer_head.h> /* for try_to_release_page() */
#include <linux/percpu.h>
......@@ -370,6 +371,7 @@ void vm_acct_memory(long pages)
}
preempt_enable();
}
EXPORT_SYMBOL(vm_acct_memory);
#endif
......
......@@ -20,7 +20,9 @@
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/rmap-locking.h>
#include <linux/security.h>
#include <asm/pgtable.h>
#include <linux/swapops.h>
......@@ -30,6 +32,8 @@ unsigned int nr_swapfiles;
int total_swap_pages;
static int swap_overflow;
EXPORT_SYMBOL(total_swap_pages);
static const char Bad_file[] = "Bad swap file entry ";
static const char Unused_file[] = "Unused swap file entry ";
static const char Bad_offset[] = "Bad swap offset entry ";
......@@ -1042,7 +1046,7 @@ asmlinkage long sys_swapoff(const char __user * specialfile)
swap_list_unlock();
goto out_dput;
}
if (vm_enough_memory(p->pages))
if (!security_vm_enough_memory(p->pages))
vm_unacct_memory(p->pages);
else {
err = -ENOMEM;
......
......@@ -15,6 +15,9 @@
#include <linux/security.h>
#include <linux/file.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/pagemap.h>
#include <linux/swap.h>
#include <linux/smp_lock.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
......@@ -275,6 +278,65 @@ int cap_syslog (int type)
return 0;
}
/*
* Check that a process has enough memory to allocate a new virtual
* mapping. 0 means there is enough memory for the allocation to
* succeed and -ENOMEM implies there is not.
*
* We currently support three overcommit policies, which are set via the
* vm.overcommit_memory sysctl. See Documentation/vm/overcommit-acounting
*
* Strict overcommit modes added 2002 Feb 26 by Alan Cox.
* Additional code 2002 Jul 20 by Robert Love.
*/
int cap_vm_enough_memory(long pages)
{
unsigned long free, allowed;
vm_acct_memory(pages);
/*
* Sometimes we want to use more memory than we have
*/
if (sysctl_overcommit_memory == 1)
return 0;
if (sysctl_overcommit_memory == 0) {
free = get_page_cache_size();
free += nr_free_pages();
free += nr_swap_pages;
/*
* Any slabs which are created with the
* SLAB_RECLAIM_ACCOUNT flag claim to have contents
* which are reclaimable, under pressure. The dentry
* cache and most inode caches should fall into this
*/
free += atomic_read(&slab_reclaim_pages);
/*
* Leave the last 3% for root
*/
if (!capable(CAP_SYS_ADMIN))
free -= free / 32;
if (free > pages)
return 0;
vm_unacct_memory(pages);
return -ENOMEM;
}
allowed = totalram_pages * sysctl_overcommit_ratio / 100;
allowed += total_swap_pages;
if (atomic_read(&vm_committed_space) < allowed)
return 0;
vm_unacct_memory(pages);
return -ENOMEM;
}
EXPORT_SYMBOL(cap_capable);
EXPORT_SYMBOL(cap_ptrace);
EXPORT_SYMBOL(cap_capget);
......@@ -286,6 +348,7 @@ EXPORT_SYMBOL(cap_bprm_secureexec);
EXPORT_SYMBOL(cap_task_post_setuid);
EXPORT_SYMBOL(cap_task_reparent_to_init);
EXPORT_SYMBOL(cap_syslog);
EXPORT_SYMBOL(cap_vm_enough_memory);
#ifdef CONFIG_SECURITY
......@@ -307,6 +370,8 @@ static struct security_operations capability_ops = {
.task_reparent_to_init = cap_task_reparent_to_init,
.syslog = cap_syslog,
.vm_enough_memory = cap_vm_enough_memory,
};
#if defined(CONFIG_SECURITY_CAPABILITIES_MODULE)
......
......@@ -17,6 +17,9 @@
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mman.h>
#include <linux/pagemap.h>
#include <linux/swap.h>
#include <linux/security.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
......@@ -97,6 +100,54 @@ static int dummy_syslog (int type)
return 0;
}
static int dummy_vm_enough_memory(long pages)
{
unsigned long free, allowed;
vm_acct_memory(pages);
/*
* Sometimes we want to use more memory than we have
*/
if (sysctl_overcommit_memory == 1)
return 0;
if (sysctl_overcommit_memory == 0) {
free = get_page_cache_size();
free += nr_free_pages();
free += nr_swap_pages;
/*
* Any slabs which are created with the
* SLAB_RECLAIM_ACCOUNT flag claim to have contents
* which are reclaimable, under pressure. The dentry
* cache and most inode caches should fall into this
*/
free += atomic_read(&slab_reclaim_pages);
/*
* Leave the last 3% for root
*/
if (current->euid)
free -= free / 32;
if (free > pages)
return 0;
vm_unacct_memory(pages);
return -ENOMEM;
}
allowed = totalram_pages * sysctl_overcommit_ratio / 100;
allowed += total_swap_pages;
if (atomic_read(&vm_committed_space) < allowed)
return 0;
vm_unacct_memory(pages);
return -ENOMEM;
}
static int dummy_bprm_alloc_security (struct linux_binprm *bprm)
{
return 0;
......@@ -793,6 +844,7 @@ void security_fixup_ops (struct security_operations *ops)
set_to_dummy_if_null(ops, quota_on);
set_to_dummy_if_null(ops, sysctl);
set_to_dummy_if_null(ops, syslog);
set_to_dummy_if_null(ops, vm_enough_memory);
set_to_dummy_if_null(ops, bprm_alloc_security);
set_to_dummy_if_null(ops, bprm_free_security);
set_to_dummy_if_null(ops, bprm_compute_creds);
......
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