@@ -1029,6 +1029,15 @@ defined, these additional entries are present:
number of cycles between interrupts used to feed the pool.
+randomize_mremap
+==================
+
+This option, when set, can be used to force mremap(...,
+MREMAP_MAYMOVE) to always move the mappings even if not necessary.
+In addition to improved address space layout randomization, this can
+expose bugs where the caller is not actually expecting a moved
+mapping, even though this may sometimes happen without this flag.
+
randomize_va_space
==================
@@ -2990,6 +2990,8 @@ void drop_slab_node(int nid);
extern int randomize_va_space;
#endif
+extern int randomize_mremap;
+
const char * arch_vma_name(struct vm_area_struct *vma);
#ifdef CONFIG_MMU
void print_vma_addr(char *prefix, unsigned long rip);
@@ -2429,6 +2429,13 @@ static struct ctl_table kern_table[] = {
.mode = 0644,
.proc_handler = proc_dointvec,
},
+ {
+ .procname = "randomize_mremap",
+ .data = &randomize_mremap,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
#endif
#if defined(CONFIG_S390) && defined(CONFIG_SMP)
{
@@ -648,6 +648,14 @@ static int vma_expandable(struct vm_area_struct *vma, unsigned long delta)
return 1;
}
+/*
+ * Force mremap(..., MREMAP_MAYMOVE) to always move the mappings even
+ * if not necessary. This can expose bugs where the caller is not
+ * actually expecting a moved mapping, even though this may sometimes
+ * happen without this flag.
+ */
+int randomize_mremap __read_mostly = 0;
+
/*
* Expand (or shrink) an existing mapping, potentially moving it at the
* same time (controlled by the MREMAP_MAYMOVE flag and available VM space)
@@ -665,6 +673,7 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
unsigned long charged = 0;
bool locked = false;
bool downgraded = false;
+ bool randomize = false;
struct vm_userfaultfd_ctx uf = NULL_VM_UFFD_CTX;
LIST_HEAD(uf_unmap_early);
LIST_HEAD(uf_unmap);
@@ -720,6 +729,7 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
goto out;
}
+ randomize = (flags & MREMAP_MAYMOVE) && randomize_mremap;
/*
* Always allow a shrinking remap: that just unmaps
* the unnecessary pages..
@@ -730,7 +740,7 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
int retval;
retval = __do_munmap(mm, addr+new_len, old_len - new_len,
- &uf_unmap, true);
+ &uf_unmap, !randomize);
if (retval < 0 && old_len != new_len) {
ret = retval;
goto out;
@@ -738,6 +748,16 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
} else if (retval == 1)
downgraded = true;
ret = addr;
+
+ /*
+ * Caller is happy with a new address, so let's move
+ * even if not necessary
+ */
+ if (randomize)
+ ret = mremap_to(addr, new_len, 0, new_len,
+ &locked, flags, &uf, &uf_unmap_early,
+ &uf_unmap);
+
goto out;
}
@@ -751,8 +771,10 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
}
/* old_len exactly to the end of the area..
+ * But when randomizing, don't just expand the mapping if
+ * caller is happy with a moved and resized mapping
*/
- if (old_len == vma->vm_end - addr) {
+ if (old_len == vma->vm_end - addr && !randomize) {
/* can we just expand the current mapping? */
if (vma_expandable(vma, new_len - old_len)) {
int pages = (new_len - old_len) >> PAGE_SHIFT;
New sysctl kernel.randomize_mremap, when set, can be used to force mremap(..., MREMAP_MAYMOVE) to always move the mappings even if not necessary. In addition to improved address space layout randomization, this can expose bugs where the caller is not actually expecting a moved mapping, even though this may sometimes happen without this flag. Example: $ cat mremap.c #define _GNU_SOURCE #include <stddef.h> #include <sys/mman.h> int main(void) { void *addr = mmap(NULL, 4096, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); addr = mremap(addr, 4096, 8192, MREMAP_MAYMOVE); mremap(addr, 4096, 4096, MREMAP_MAYMOVE); return 0; } $ gcc -o mremap mremap.c $ strace -e mmap,mremap ./mremap mmap(NULL, 4096, PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x68a16298d000 mremap(0x68a16298d000, 4096, 8192, MREMAP_MAYMOVE) = 0x68a16298d000 mremap(0x68a16298d000, 4096, 4096, MREMAP_MAYMOVE) = 0x68a16298d000 Setting the sysctl enables randomization: $ sudo sysctl kernel.randomize_mremap=1 $ strace -e mmap,mremap ./mremap mmap(NULL, 4096, PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x6366429cf000 mremap(0x6366429cf000, 4096, 8192, MREMAP_MAYMOVE) = 0x70aa47ad0000 mremap(0x70aa47ad0000, 4096, 4096, MREMAP_MAYMOVE) = 0x5b37dc166000 CC: Andrew Morton <akpm@linux-foundation.org> CC: Jann Horn <jannh@google.com> CC: Kees Cook <keescook@chromium.org> CC: Matthew Wilcox <willy@infradead.org> CC: Mike Rapoport <rppt@kernel.org> CC: Linux API <linux-api@vger.kernel.org> Signed-off-by: Topi Miettinen <toiwoton@gmail.com> --- Documentation/admin-guide/sysctl/kernel.rst | 9 +++++++ include/linux/mm.h | 2 ++ kernel/sysctl.c | 7 ++++++ mm/mremap.c | 26 +++++++++++++++++++-- 4 files changed, 42 insertions(+), 2 deletions(-)