#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>
        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()
  */
 
 #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>
 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
  */
        opts->num_mnt_opts = 0;
 }
 
+/*
+ * 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);
 /**
  * struct security_operations - main security structure
  *
 
 #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>
 
        {
                .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
        {
 
          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.
 
 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
 
 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);
 
 
          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
 
 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
 
 {
        int ret = 0;
 
-       if (addr < mmap_min_addr) {
+       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 */
 
--- /dev/null
+#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);
 
         * at bad behaviour/exploit that we always want to get the AVC, even
         * if DAC would have also denied the operation.
         */
-       if (addr < mmap_min_addr) {
+       if (addr < CONFIG_LSM_MMAP_MIN_ADDR) {
                rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT,
                                  MEMPROTECT__MMAP_ZERO, NULL);
                if (rc)