From owner-svn-src-head@freebsd.org Sat May 18 02:02:16 2019 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id E7A7715A5325; Sat, 18 May 2019 02:02:15 +0000 (UTC) (envelope-from markj@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 8A8A2932AF; Sat, 18 May 2019 02:02:15 +0000 (UTC) (envelope-from markj@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 647C418FCC; Sat, 18 May 2019 02:02:15 +0000 (UTC) (envelope-from markj@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id x4I22FHs040762; Sat, 18 May 2019 02:02:15 GMT (envelope-from markj@FreeBSD.org) Received: (from markj@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x4I22FH6040761; Sat, 18 May 2019 02:02:15 GMT (envelope-from markj@FreeBSD.org) Message-Id: <201905180202.x4I22FH6040761@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: markj set sender to markj@FreeBSD.org using -f From: Mark Johnston Date: Sat, 18 May 2019 02:02:15 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r347950 - head/sys/vm X-SVN-Group: head X-SVN-Commit-Author: markj X-SVN-Commit-Paths: head/sys/vm X-SVN-Commit-Revision: 347950 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Rspamd-Queue-Id: 8A8A2932AF X-Spamd-Bar: -- Authentication-Results: mx1.freebsd.org X-Spamd-Result: default: False [-2.97 / 15.00]; local_wl_from(0.00)[FreeBSD.org]; NEURAL_HAM_MEDIUM(-1.00)[-0.999,0]; NEURAL_HAM_SHORT(-0.97)[-0.973,0]; ASN(0.00)[asn:11403, ipnet:2610:1c1:1::/48, country:US]; NEURAL_HAM_LONG(-1.00)[-1.000,0] X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 18 May 2019 02:02:16 -0000 Author: markj Date: Sat May 18 02:02:14 2019 New Revision: 347950 URL: https://svnweb.freebsd.org/changeset/base/347950 Log: Use M_NEXTFIT in memguard(9). memguard(9) wants to avoid reuse of freed addresses for as long as possible. Previously it maintained a racily updated cursor which was passed to vmem_xalloc(9) as the minimum address. However, vmem will not in general return the lowest free address in the arena, so this trick only really works until the cursor has wrapped around the first time. Reported by: alc Reviewed by: alc MFC after: 1 month Differential Revision: https://reviews.freebsd.org/D17227 Modified: head/sys/vm/memguard.c Modified: head/sys/vm/memguard.c ============================================================================== --- head/sys/vm/memguard.c Sat May 18 01:46:38 2019 (r347949) +++ head/sys/vm/memguard.c Sat May 18 02:02:14 2019 (r347950) @@ -102,26 +102,29 @@ SYSCTL_PROC(_vm_memguard, OID_AUTO, desc, CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 0, memguard_sysctl_desc, "A", "Short description of memory type to monitor"); -static vm_offset_t memguard_cursor; +static int +memguard_sysctl_mapused(SYSCTL_HANDLER_ARGS) +{ + vmem_size_t size; + + size = vmem_size(memguard_arena, VMEM_ALLOC); + return (sysctl_handle_long(oidp, &size, sizeof(size), req)); +} + static vm_offset_t memguard_base; static vm_size_t memguard_mapsize; static vm_size_t memguard_physlimit; static u_long memguard_wasted; -static u_long memguard_wrap; static u_long memguard_succ; static u_long memguard_fail_kva; static u_long memguard_fail_pgs; -SYSCTL_ULONG(_vm_memguard, OID_AUTO, cursor, CTLFLAG_RD, - &memguard_cursor, 0, "MemGuard cursor"); SYSCTL_ULONG(_vm_memguard, OID_AUTO, mapsize, CTLFLAG_RD, &memguard_mapsize, 0, "MemGuard private arena size"); SYSCTL_ULONG(_vm_memguard, OID_AUTO, phys_limit, CTLFLAG_RD, &memguard_physlimit, 0, "Limit on MemGuard memory consumption"); SYSCTL_ULONG(_vm_memguard, OID_AUTO, wasted, CTLFLAG_RD, &memguard_wasted, 0, "Excess memory used through page promotion"); -SYSCTL_ULONG(_vm_memguard, OID_AUTO, wrapcnt, CTLFLAG_RD, - &memguard_wrap, 0, "MemGuard cursor wrap count"); SYSCTL_ULONG(_vm_memguard, OID_AUTO, numalloc, CTLFLAG_RD, &memguard_succ, 0, "Count of successful MemGuard allocations"); SYSCTL_ULONG(_vm_memguard, OID_AUTO, fail_kva, CTLFLAG_RD, @@ -157,7 +160,7 @@ SYSCTL_ULONG(_vm_memguard, OID_AUTO, frequency_hits, C /* * Return a fudged value to be used for vm_kmem_size for allocating - * the kernel_arena. The memguard memory will be a submap. + * the kernel_arena. */ unsigned long memguard_fudge(unsigned long km_size, const struct vm_map *parent_map) @@ -199,7 +202,8 @@ memguard_fudge(unsigned long km_size, const struct vm_ /* * Initialize the MemGuard mock allocator. All objects from MemGuard come - * out of a single VM map (contiguous chunk of address space). + * out of a single contiguous chunk of kernel address space that is managed + * by a vmem arena. */ void memguard_init(vmem_t *parent) @@ -209,7 +213,6 @@ memguard_init(vmem_t *parent) vmem_alloc(parent, memguard_mapsize, M_BESTFIT | M_WAITOK, &base); vmem_init(memguard_arena, "memguard arena", base, memguard_mapsize, PAGE_SIZE, 0, M_WAITOK); - memguard_cursor = base; memguard_base = base; printf("MEMGUARD DEBUGGING ALLOCATOR INITIALIZED:\n"); @@ -227,15 +230,15 @@ memguard_sysinit(void) struct sysctl_oid_list *parent; parent = SYSCTL_STATIC_CHILDREN(_vm_memguard); - - SYSCTL_ADD_UAUTO(NULL, parent, OID_AUTO, "mapstart", CTLFLAG_RD, - &memguard_base, "MemGuard KVA base"); - SYSCTL_ADD_UAUTO(NULL, parent, OID_AUTO, "maplimit", CTLFLAG_RD, - &memguard_mapsize, "MemGuard KVA size"); -#if 0 - SYSCTL_ADD_ULONG(NULL, parent, OID_AUTO, "mapused", CTLFLAG_RD, - &memguard_map->size, "MemGuard KVA used"); -#endif + SYSCTL_ADD_UAUTO(NULL, parent, OID_AUTO, "mapstart", + CTLFLAG_RD, &memguard_base, + "MemGuard KVA base"); + SYSCTL_ADD_UAUTO(NULL, parent, OID_AUTO, "maplimit", + CTLFLAG_RD, &memguard_mapsize, + "MemGuard KVA size"); + SYSCTL_ADD_PROC(NULL, parent, OID_AUTO, "mapused", + CTLFLAG_RD | CTLTYPE_ULONG, NULL, 0, memguard_sysctl_mapused, "LU", + "MemGuard KVA used"); } SYSINIT(memguard, SI_SUB_KLD, SI_ORDER_ANY, memguard_sysinit, NULL); @@ -288,17 +291,16 @@ memguard_alloc(unsigned long req_size, int flags) { vm_offset_t addr, origaddr; u_long size_p, size_v; - int do_guard, rv; + int do_guard, error, rv; size_p = round_page(req_size); if (size_p == 0) return (NULL); + /* * To ensure there are holes on both sides of the allocation, - * request 2 extra pages of KVA. We will only actually add a - * vm_map_entry and get pages for the original request. Save - * the value of memguard_options so we have a consistent - * value. + * request 2 extra pages of KVA. Save the value of memguard_options + * so that we use a consistent value throughout this function. */ size_v = size_p; do_guard = (memguard_options & MG_GUARD_AROUND) != 0; @@ -317,33 +319,17 @@ memguard_alloc(unsigned long req_size, int flags) memguard_fail_pgs++; goto out; } + /* - * Keep a moving cursor so we don't recycle KVA as long as - * possible. It's not perfect, since we don't know in what - * order previous allocations will be free'd, but it's simple - * and fast, and requires O(1) additional storage if guard - * pages are not used. - * - * XXX This scheme will lead to greater fragmentation of the - * map, unless vm_map_findspace() is tweaked. + * Attempt to avoid address reuse for as long as possible, to increase + * the likelihood of catching a use-after-free. */ - for (;;) { - if (vmem_xalloc(memguard_arena, size_v, 0, 0, 0, - memguard_cursor, VMEM_ADDR_MAX, - M_BESTFIT | M_NOWAIT, &origaddr) == 0) - break; - /* - * The map has no space. This may be due to - * fragmentation, or because the cursor is near the - * end of the map. - */ - if (memguard_cursor == memguard_base) { - memguard_fail_kva++; - addr = (vm_offset_t)NULL; - goto out; - } - memguard_wrap++; - memguard_cursor = memguard_base; + error = vmem_alloc(memguard_arena, size_v, M_NEXTFIT | M_NOWAIT, + &origaddr); + if (error != 0) { + memguard_fail_kva++; + addr = (vm_offset_t)NULL; + goto out; } addr = origaddr; if (do_guard) @@ -355,7 +341,6 @@ memguard_alloc(unsigned long req_size, int flags) addr = (vm_offset_t)NULL; goto out; } - memguard_cursor = addr + size_v; *v2sizep(trunc_page(addr)) = req_size; *v2sizev(trunc_page(addr)) = size_v; memguard_succ++;