git: d8e6f4946cec - main - vm: Fix anonymous memory clustering under ASLR

From: Alan Cox <alc_at_FreeBSD.org>
Date: Tue, 27 Jun 2023 04:43:50 UTC
The branch main has been updated by alc:

URL: https://cgit.FreeBSD.org/src/commit/?id=d8e6f4946cec0b84a6997d62e791b8cf993741b2

commit d8e6f4946cec0b84a6997d62e791b8cf993741b2
Author:     Alan Cox <alc@FreeBSD.org>
AuthorDate: 2023-06-23 17:00:32 +0000
Commit:     Alan Cox <alc@FreeBSD.org>
CommitDate: 2023-06-27 04:42:48 +0000

    vm: Fix anonymous memory clustering under ASLR
    
    By default, our ASLR implementation is supposed to cluster anonymous
    memory allocations, unless the application's mmap(..., MAP_ANON, ...)
    call included a non-zero address hint.  Unfortunately, clustering
    never occurred because kern_mmap() always replaced the given address
    hint when it was zero.  So, the ASLR implementation always believed
    that a non-zero hint had been provided and randomized the mapping's
    location in the address space.  To fix this problem, I'm pushing down
    the point at which we convert a hint of zero to the minimum allocatable
    address from kern_mmap() to vm_map_find_min().
    
    Reviewed by:    kib
    MFC after:      2 weeks
    Differential Revision:  https://reviews.freebsd.org/D40743
---
 sys/vm/vm_map.c  | 10 +++++++---
 sys/vm/vm_map.h  |  1 +
 sys/vm/vm_mmap.c |  8 +++++---
 3 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/sys/vm/vm_map.c b/sys/vm/vm_map.c
index f5863a9b9939..a02107b5e64d 100644
--- a/sys/vm/vm_map.c
+++ b/sys/vm/vm_map.c
@@ -1981,14 +1981,14 @@ SYSCTL_INT(_vm, OID_AUTO, cluster_anon, CTLFLAG_RW,
     "Cluster anonymous mappings: 0 = no, 1 = yes if no hint, 2 = always");
 
 static bool
-clustering_anon_allowed(vm_offset_t addr)
+clustering_anon_allowed(vm_offset_t addr, int cow)
 {
 
 	switch (cluster_anon) {
 	case 0:
 		return (false);
 	case 1:
-		return (addr == 0);
+		return (addr == 0 || (cow & MAP_NO_HINT) != 0);
 	case 2:
 	default:
 		return (true);
@@ -2111,7 +2111,7 @@ vm_map_find(vm_map_t map, vm_object_t object, vm_ooffset_t offset,
 	} else
 		alignment = 0;
 	en_aslr = (map->flags & MAP_ASLR) != 0;
-	update_anon = cluster = clustering_anon_allowed(*addr) &&
+	update_anon = cluster = clustering_anon_allowed(*addr, cow) &&
 	    (map->flags & MAP_IS_SUB_MAP) == 0 && max_addr == 0 &&
 	    find_space != VMFS_NO_SPACE && object == NULL &&
 	    (cow & (MAP_INHERIT_SHARE | MAP_STACK_GROWS_UP |
@@ -2255,6 +2255,10 @@ vm_map_find_min(vm_map_t map, vm_object_t object, vm_ooffset_t offset,
 	int rv;
 
 	hint = *addr;
+	if (hint == 0)
+		cow |= MAP_NO_HINT;
+	if (hint < min_addr)
+		*addr = hint = min_addr;
 	for (;;) {
 		rv = vm_map_find(map, object, offset, addr, length, max_addr,
 		    find_space, prot, max, cow);
diff --git a/sys/vm/vm_map.h b/sys/vm/vm_map.h
index 2ac54a39a57b..fd8b606e8ddc 100644
--- a/sys/vm/vm_map.h
+++ b/sys/vm/vm_map.h
@@ -383,6 +383,7 @@ long vmspace_resident_count(struct vmspace *vmspace);
 #define	MAP_CREATE_STACK_GAP_DN	0x00020000
 #define	MAP_VN_EXEC		0x00040000
 #define	MAP_SPLIT_BOUNDARY_MASK	0x00180000
+#define	MAP_NO_HINT		0x00200000
 
 #define	MAP_SPLIT_BOUNDARY_SHIFT 19
 
diff --git a/sys/vm/vm_mmap.c b/sys/vm/vm_mmap.c
index 56345fcaf560..408e077476dd 100644
--- a/sys/vm/vm_mmap.c
+++ b/sys/vm/vm_mmap.c
@@ -353,10 +353,12 @@ kern_mmap(struct thread *td, const struct mmap_req *mrp)
 		 * the hint would fall in the potential heap space,
 		 * place it after the end of the largest possible heap.
 		 *
-		 * There should really be a pmap call to determine a reasonable
-		 * location.
+		 * For anonymous mappings within the address space of the
+		 * calling process, the absence of a hint is handled at a
+		 * lower level in order to implement different clustering
+		 * strategies for ASLR.
 		 */
-		if (addr == 0 ||
+		if (((flags & MAP_ANON) == 0 && addr == 0) ||
 		    (addr >= round_page((vm_offset_t)vms->vm_taddr) &&
 		    addr < round_page((vm_offset_t)vms->vm_daddr +
 		    lim_max(td, RLIMIT_DATA))))