Home | History | Annotate | Download | only in openssh
      1 /* $OpenBSD: monitor_mm.c,v 1.21 2015/02/06 23:21:59 millert Exp $ */
      2 /*
      3  * Copyright 2002 Niels Provos <provos (at) citi.umich.edu>
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 #include "includes.h"
     28 
     29 #include <sys/types.h>
     30 #ifdef HAVE_SYS_MMAN_H
     31 #include <sys/mman.h>
     32 #endif
     33 #include "openbsd-compat/sys-tree.h"
     34 
     35 #include <errno.h>
     36 #include <stdarg.h>
     37 #include <stddef.h>
     38 #ifdef HAVE_STDINT_H
     39 #include <stdint.h>
     40 #endif
     41 #include <stdlib.h>
     42 #include <string.h>
     43 
     44 #include "xmalloc.h"
     45 #include "ssh.h"
     46 #include "log.h"
     47 #include "monitor_mm.h"
     48 
     49 static int
     50 mm_compare(struct mm_share *a, struct mm_share *b)
     51 {
     52 	ptrdiff_t diff = (char *)a->address - (char *)b->address;
     53 
     54 	if (diff == 0)
     55 		return (0);
     56 	else if (diff < 0)
     57 		return (-1);
     58 	else
     59 		return (1);
     60 }
     61 
     62 RB_GENERATE(mmtree, mm_share, next, mm_compare)
     63 
     64 static struct mm_share *
     65 mm_make_entry(struct mm_master *mm, struct mmtree *head,
     66     void *address, size_t size)
     67 {
     68 	struct mm_share *tmp, *tmp2;
     69 
     70 	if (mm->mmalloc == NULL)
     71 		tmp = xcalloc(1, sizeof(struct mm_share));
     72 	else
     73 		tmp = mm_xmalloc(mm->mmalloc, sizeof(struct mm_share));
     74 	tmp->address = address;
     75 	tmp->size = size;
     76 
     77 	tmp2 = RB_INSERT(mmtree, head, tmp);
     78 	if (tmp2 != NULL)
     79 		fatal("mm_make_entry(%p): double address %p->%p(%zu)",
     80 		    mm, tmp2, address, size);
     81 
     82 	return (tmp);
     83 }
     84 
     85 /* Creates a shared memory area of a certain size */
     86 
     87 struct mm_master *
     88 mm_create(struct mm_master *mmalloc, size_t size)
     89 {
     90 	void *address;
     91 	struct mm_master *mm;
     92 
     93 	if (mmalloc == NULL)
     94 		mm = xcalloc(1, sizeof(struct mm_master));
     95 	else
     96 		mm = mm_xmalloc(mmalloc, sizeof(struct mm_master));
     97 
     98 	/*
     99 	 * If the memory map has a mm_master it can be completely
    100 	 * shared including authentication between the child
    101 	 * and the client.
    102 	 */
    103 	mm->mmalloc = mmalloc;
    104 
    105 	address = xmmap(size);
    106 	if (address == (void *)MAP_FAILED)
    107 		fatal("mmap(%zu): %s", size, strerror(errno));
    108 
    109 	mm->address = address;
    110 	mm->size = size;
    111 
    112 	RB_INIT(&mm->rb_free);
    113 	RB_INIT(&mm->rb_allocated);
    114 
    115 	mm_make_entry(mm, &mm->rb_free, address, size);
    116 
    117 	return (mm);
    118 }
    119 
    120 /* Frees either the allocated or the free list */
    121 
    122 static void
    123 mm_freelist(struct mm_master *mmalloc, struct mmtree *head)
    124 {
    125 	struct mm_share *mms, *next;
    126 
    127 	for (mms = RB_ROOT(head); mms; mms = next) {
    128 		next = RB_NEXT(mmtree, head, mms);
    129 		RB_REMOVE(mmtree, head, mms);
    130 		if (mmalloc == NULL)
    131 			free(mms);
    132 		else
    133 			mm_free(mmalloc, mms);
    134 	}
    135 }
    136 
    137 /* Destroys a memory mapped area */
    138 
    139 void
    140 mm_destroy(struct mm_master *mm)
    141 {
    142 	mm_freelist(mm->mmalloc, &mm->rb_free);
    143 	mm_freelist(mm->mmalloc, &mm->rb_allocated);
    144 
    145 #ifdef HAVE_MMAP
    146 	if (munmap(mm->address, mm->size) == -1)
    147 		fatal("munmap(%p, %zu): %s", mm->address, mm->size,
    148 		    strerror(errno));
    149 #else
    150 	fatal("%s: UsePrivilegeSeparation=yes and Compression=yes not supported",
    151 	    __func__);
    152 #endif
    153 	if (mm->mmalloc == NULL)
    154 		free(mm);
    155 	else
    156 		mm_free(mm->mmalloc, mm);
    157 }
    158 
    159 void *
    160 mm_xmalloc(struct mm_master *mm, size_t size)
    161 {
    162 	void *address;
    163 
    164 	address = mm_malloc(mm, size);
    165 	if (address == NULL)
    166 		fatal("%s: mm_malloc(%zu)", __func__, size);
    167 	memset(address, 0, size);
    168 	return (address);
    169 }
    170 
    171 
    172 /* Allocates data from a memory mapped area */
    173 
    174 void *
    175 mm_malloc(struct mm_master *mm, size_t size)
    176 {
    177 	struct mm_share *mms, *tmp;
    178 
    179 	if (size == 0)
    180 		fatal("mm_malloc: try to allocate 0 space");
    181 	if (size > SIZE_MAX - MM_MINSIZE + 1)
    182 		fatal("mm_malloc: size too big");
    183 
    184 	size = ((size + (MM_MINSIZE - 1)) / MM_MINSIZE) * MM_MINSIZE;
    185 
    186 	RB_FOREACH(mms, mmtree, &mm->rb_free) {
    187 		if (mms->size >= size)
    188 			break;
    189 	}
    190 
    191 	if (mms == NULL)
    192 		return (NULL);
    193 
    194 	/* Debug */
    195 	memset(mms->address, 0xd0, size);
    196 
    197 	tmp = mm_make_entry(mm, &mm->rb_allocated, mms->address, size);
    198 
    199 	/* Does not change order in RB tree */
    200 	mms->size -= size;
    201 	mms->address = (char *)mms->address + size;
    202 
    203 	if (mms->size == 0) {
    204 		RB_REMOVE(mmtree, &mm->rb_free, mms);
    205 		if (mm->mmalloc == NULL)
    206 			free(mms);
    207 		else
    208 			mm_free(mm->mmalloc, mms);
    209 	}
    210 
    211 	return (tmp->address);
    212 }
    213 
    214 /* Frees memory in a memory mapped area */
    215 
    216 void
    217 mm_free(struct mm_master *mm, void *address)
    218 {
    219 	struct mm_share *mms, *prev, tmp;
    220 
    221 	tmp.address = address;
    222 	mms = RB_FIND(mmtree, &mm->rb_allocated, &tmp);
    223 	if (mms == NULL)
    224 		fatal("mm_free(%p): can not find %p", mm, address);
    225 
    226 	/* Debug */
    227 	memset(mms->address, 0xd0, mms->size);
    228 
    229 	/* Remove from allocated list and insert in free list */
    230 	RB_REMOVE(mmtree, &mm->rb_allocated, mms);
    231 	if (RB_INSERT(mmtree, &mm->rb_free, mms) != NULL)
    232 		fatal("mm_free(%p): double address %p", mm, address);
    233 
    234 	/* Find previous entry */
    235 	prev = mms;
    236 	if (RB_LEFT(prev, next)) {
    237 		prev = RB_LEFT(prev, next);
    238 		while (RB_RIGHT(prev, next))
    239 			prev = RB_RIGHT(prev, next);
    240 	} else {
    241 		if (RB_PARENT(prev, next) &&
    242 		    (prev == RB_RIGHT(RB_PARENT(prev, next), next)))
    243 			prev = RB_PARENT(prev, next);
    244 		else {
    245 			while (RB_PARENT(prev, next) &&
    246 			    (prev == RB_LEFT(RB_PARENT(prev, next), next)))
    247 				prev = RB_PARENT(prev, next);
    248 			prev = RB_PARENT(prev, next);
    249 		}
    250 	}
    251 
    252 	/* Check if range does not overlap */
    253 	if (prev != NULL && MM_ADDRESS_END(prev) > address)
    254 		fatal("mm_free: memory corruption: %p(%zu) > %p",
    255 		    prev->address, prev->size, address);
    256 
    257 	/* See if we can merge backwards */
    258 	if (prev != NULL && MM_ADDRESS_END(prev) == address) {
    259 		prev->size += mms->size;
    260 		RB_REMOVE(mmtree, &mm->rb_free, mms);
    261 		if (mm->mmalloc == NULL)
    262 			free(mms);
    263 		else
    264 			mm_free(mm->mmalloc, mms);
    265 	} else
    266 		prev = mms;
    267 
    268 	if (prev == NULL)
    269 		return;
    270 
    271 	/* Check if we can merge forwards */
    272 	mms = RB_NEXT(mmtree, &mm->rb_free, prev);
    273 	if (mms == NULL)
    274 		return;
    275 
    276 	if (MM_ADDRESS_END(prev) > mms->address)
    277 		fatal("mm_free: memory corruption: %p < %p(%zu)",
    278 		    mms->address, prev->address, prev->size);
    279 	if (MM_ADDRESS_END(prev) != mms->address)
    280 		return;
    281 
    282 	prev->size += mms->size;
    283 	RB_REMOVE(mmtree, &mm->rb_free, mms);
    284 
    285 	if (mm->mmalloc == NULL)
    286 		free(mms);
    287 	else
    288 		mm_free(mm->mmalloc, mms);
    289 }
    290 
    291 static void
    292 mm_sync_list(struct mmtree *oldtree, struct mmtree *newtree,
    293     struct mm_master *mm, struct mm_master *mmold)
    294 {
    295 	struct mm_master *mmalloc = mm->mmalloc;
    296 	struct mm_share *mms, *new;
    297 
    298 	/* Sync free list */
    299 	RB_FOREACH(mms, mmtree, oldtree) {
    300 		/* Check the values */
    301 		mm_memvalid(mmold, mms, sizeof(struct mm_share));
    302 		mm_memvalid(mm, mms->address, mms->size);
    303 
    304 		new = mm_xmalloc(mmalloc, sizeof(struct mm_share));
    305 		memcpy(new, mms, sizeof(struct mm_share));
    306 		RB_INSERT(mmtree, newtree, new);
    307 	}
    308 }
    309 
    310 void
    311 mm_share_sync(struct mm_master **pmm, struct mm_master **pmmalloc)
    312 {
    313 	struct mm_master *mm;
    314 	struct mm_master *mmalloc;
    315 	struct mm_master *mmold;
    316 	struct mmtree rb_free, rb_allocated;
    317 
    318 	debug3("%s: Share sync", __func__);
    319 
    320 	mm = *pmm;
    321 	mmold = mm->mmalloc;
    322 	mm_memvalid(mmold, mm, sizeof(*mm));
    323 
    324 	mmalloc = mm_create(NULL, mm->size);
    325 	mm = mm_xmalloc(mmalloc, sizeof(struct mm_master));
    326 	memcpy(mm, *pmm, sizeof(struct mm_master));
    327 	mm->mmalloc = mmalloc;
    328 
    329 	rb_free = mm->rb_free;
    330 	rb_allocated = mm->rb_allocated;
    331 
    332 	RB_INIT(&mm->rb_free);
    333 	RB_INIT(&mm->rb_allocated);
    334 
    335 	mm_sync_list(&rb_free, &mm->rb_free, mm, mmold);
    336 	mm_sync_list(&rb_allocated, &mm->rb_allocated, mm, mmold);
    337 
    338 	mm_destroy(mmold);
    339 
    340 	*pmm = mm;
    341 	*pmmalloc = mmalloc;
    342 
    343 	debug3("%s: Share sync end", __func__);
    344 }
    345 
    346 void
    347 mm_memvalid(struct mm_master *mm, void *address, size_t size)
    348 {
    349 	void *end = (char *)address + size;
    350 
    351 	if (address < mm->address)
    352 		fatal("mm_memvalid: address too small: %p", address);
    353 	if (end < address)
    354 		fatal("mm_memvalid: end < address: %p < %p", end, address);
    355 	if (end > MM_ADDRESS_END(mm))
    356 		fatal("mm_memvalid: address too large: %p", address);
    357 }
    358