CVE-2016-10012: Improper Restriction of Operations within the Bounds of a Memory Buffer (CWE-119)
value: The shared memory manager (associated with pre-authentication compression) in sshd in OpenSSH before 7.4 does not ensure that a bounds check is enforced by all compilers, which might allows local users to gain privileges by leveraging access to a sandboxed privilege-separation process, related to the m_zback and m_zlib data structures.
CWE description: The product performs operations on a memory buffer, but it reads from or writes to a memory location outside the buffer's intended boundary. This may result in read or write operations on unexpected memory locations that could be linked to other variables, data structures, or internal program data.
CVSS v3 base score: 7.8
Published: 2017-01-05T02:59Z
Repository: https://github.com/openbsd/src
Language: C
Files changed: usr.bin/ssh/monitor.c, usr.bin/ssh/monitor.h, usr.bin/ssh/monitor_wrap.h, usr.bin/ssh/myproposal.h, usr.bin/ssh/opacket.h, usr.bin/ssh/packet.c, usr.bin/ssh/packet.h, usr.bin/ssh/servconf.c, usr.bin/ssh/sshconnect2.c, usr.bin/ssh/sshd.c, usr.bin/ssh/sshd/Makefile
Commit date: 2016-09-28 16:33:06 +0000
Commit message: Remove support for pre-authentication compression. Doing compression
early in the protocol probably seemed reasonable in the 1990s, but
today it's clearly a bad idea in terms of both cryptography (cf.
multiple compression oracle attacks in TLS) and attack surface.

Moreover, to support it across privilege-separation zlib needed
the assistance of a complex shared-memory manager that made the
required attack surface considerably larger.

Prompted by Guido Vranken pointing out a compiler-elided security
check in the shared memory manager found by Stack
(http://css.csail.mit.edu/stack/); ok deraadt@ markus@

NB. pre-auth authentication has been disabled by default in sshd
for >10 years.

Diff: diff --git a/usr.bin/ssh/monitor.c b/usr.bin/ssh/monitor.c
index 1b14f0433b1..8421659f9c5 100644
--- a/usr.bin/ssh/monitor.c
+++ b/usr.bin/ssh/monitor.c
@@ -1,6 +1,6 @@
-/* $OpenBSD: monitor.c,v 1.165 2016/09/05 13:57:31 djm Exp $ */
+/* $OpenBSD: monitor.c,v 1.166 2016/09/28 16:33:06 djm Exp $ */
 /*
  * Copyright 2002 Niels Provos <provos@citi.umich.edu>
  * Copyright 2002 Markus Friedl <markus@openbsd.org>
  * All rights reserved.
  *
@@ -68,11 +68,10 @@
 #include "canohost.h"
 #include "log.h"
 #include "misc.h"
 #include "servconf.h"
 #include "monitor.h"
-#include "monitor_mm.h"
 #ifdef GSSAPI
 #include "ssh-gss.h"
 #endif
 #include "monitor_wrap.h"
 #include "monitor_fdpass.h"
@@ -333,35 +332,10 @@ monitor_child_postauth(struct monitor *pmonitor)
 
 	for (;;)
 		monitor_read(pmonitor, mon_dispatch, NULL);
 }
 
-void
-monitor_sync(struct monitor *pmonitor)
-{
-	if (options.compression) {
-		/* The member allocation is not visible, so sync it */
-		mm_share_sync(&pmonitor->m_zlib, &pmonitor->m_zback);
-	}
-}
-
-/* Allocation functions for zlib */
-static void *
-mm_zalloc(struct mm_master *mm, u_int ncount, u_int size)
-{
-	if (size == 0 || ncount == 0 || ncount > SIZE_MAX / size)
-		fatal("%s: mm_zalloc(%u, %u)", __func__, ncount, size);
-
-	return mm_malloc(mm, size * ncount);
-}
-
-static void
-mm_zfree(struct mm_master *mm, void *address)
-{
-	mm_free(mm, address);
-}
-
 static int
 monitor_read_log(struct monitor *pmonitor)
 {
 	Buffer logmsg;
 	u_int len, level;
@@ -1290,17 +1264,10 @@ monitor_apply_keystate(struct monitor *pmonitor)
 		kex->load_host_public_key=&get_hostkey_public_by_type;
 		kex->load_host_private_key=&get_hostkey_private_by_type;
 		kex->host_key_index=&get_hostkey_index;
 		kex->sign = sshd_hostkey_sign;
 	}
-
-	/* Update with new address */
-	if (options.compression) {
-		ssh_packet_set_compress_hooks(ssh, pmonitor->m_zlib,
-		    (ssh_packet_comp_alloc_func *)mm_zalloc,
-		    (ssh_packet_comp_free_func *)mm_zfree);
-	}
 }
 
 /* This function requries careful sanity checking */
 
 void
@@ -1349,28 +1316,15 @@ monitor_openfds(struct monitor *mon, int do_logfds)
 #define MM_MEMSIZE	65536
 
 struct monitor *
 monitor_init(void)
 {
-	struct ssh *ssh = active_state;			/* XXX */
 	struct monitor *mon;
 
 	mon = xcalloc(1, sizeof(*mon));
-
 	monitor_openfds(mon, 1);
 
-	/* Used to share zlib space across processes */
-	if (options.compression) {
-		mon->m_zback = mm_create(NULL, MM_MEMSIZE);
-		mon->m_zlib = mm_create(mon->m_zback, 20 * MM_MEMSIZE);
-
-		/* Compression needs to share state across borders */
-		ssh_packet_set_compress_hooks(ssh, mon->m_zlib,
-		    (ssh_packet_comp_alloc_func *)mm_zalloc,
-		    (ssh_packet_comp_free_func *)mm_zfree);
-	}
-
 	return mon;
 }
 
 void
 monitor_reinit(struct monitor *mon)
diff --git a/usr.bin/ssh/monitor.h b/usr.bin/ssh/monitor.h
index 0c4666629ca..5350770e7f9 100644
--- a/usr.bin/ssh/monitor.h
+++ b/usr.bin/ssh/monitor.h
@@ -1,6 +1,6 @@
-/* $OpenBSD: monitor.h,v 1.19 2015/01/19 19:52:16 markus Exp $ */
+/* $OpenBSD: monitor.h,v 1.20 2016/09/28 16:33:07 djm Exp $ */
 
 /*
  * Copyright 2002 Niels Provos <provos@citi.umich.edu>
  * All rights reserved.
  *
@@ -56,25 +56,21 @@ enum monitor_reqtype {
 	MONITOR_REQ_GSSUSEROK = 46, MONITOR_ANS_GSSUSEROK = 47,
 	MONITOR_REQ_GSSCHECKMIC = 48, MONITOR_ANS_GSSCHECKMIC = 49,
 	MONITOR_REQ_TERM = 50,
 };
 
-struct mm_master;
 struct monitor {
 	int			 m_recvfd;
 	int			 m_sendfd;
 	int			 m_log_recvfd;
 	int			 m_log_sendfd;
-	struct mm_master	*m_zback;
-	struct mm_master	*m_zlib;
 	struct kex		**m_pkex;
 	pid_t			 m_pid;
 };
 
 struct monitor *monitor_init(void);
 void monitor_reinit(struct monitor *);
-void monitor_sync(struct monitor *);
 
 struct Authctxt;
 void monitor_child_preauth(struct Authctxt *, struct monitor *);
 void monitor_child_postauth(struct monitor *);
 
diff --git a/usr.bin/ssh/monitor_mm.c b/usr.bin/ssh/monitor_mm.c
deleted file mode 100644
index a629165ed79..00000000000
--- a/usr.bin/ssh/monitor_mm.c
+++ /dev/null
@@ -1,347 +0,0 @@
-/* $OpenBSD: monitor_mm.c,v 1.21 2015/02/06 23:21:59 millert Exp $ */
-/*
- * Copyright 2002 Niels Provos <provos@citi.umich.edu>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <sys/types.h>
-#include <sys/mman.h>
-#include <sys/tree.h>
-
-#include <errno.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "xmalloc.h"
-#include "ssh.h"
-#include "log.h"
-#include "monitor_mm.h"
-
-static int
-mm_compare(struct mm_share *a, struct mm_share *b)
-{
-	ptrdiff_t diff = (char *)a->address - (char *)b->address;
-
-	if (diff == 0)
-		return (0);
-	else if (diff < 0)
-		return (-1);
-	else
-		return (1);
-}
-
-RB_GENERATE(mmtree, mm_share, next, mm_compare)
-
-static struct mm_share *
-mm_make_entry(struct mm_master *mm, struct mmtree *head,
-    void *address, size_t size)
-{
-	struct mm_share *tmp, *tmp2;
-
-	if (mm->mmalloc == NULL)
-		tmp = xcalloc(1, sizeof(struct mm_share));
-	else
-		tmp = mm_xmalloc(mm->mmalloc, sizeof(struct mm_share));
-	tmp->address = address;
-	tmp->size = size;
-
-	tmp2 = RB_INSERT(mmtree, head, tmp);
-	if (tmp2 != NULL)
-		fatal("mm_make_entry(%p): double address %p->%p(%zu)",
-		    mm, tmp2, address, size);
-
-	return (tmp);
-}
-
-/* Creates a shared memory area of a certain size */
-
-struct mm_master *
-mm_create(struct mm_master *mmalloc, size_t size)
-{
-	void *address;
-	struct mm_master *mm;
-
-	if (mmalloc == NULL)
-		mm = xcalloc(1, sizeof(struct mm_master));
-	else
-		mm = mm_xmalloc(mmalloc, sizeof(struct mm_master));
-
-	/*
-	 * If the memory map has a mm_master it can be completely
-	 * shared including authentication between the child
-	 * and the client.
-	 */
-	mm->mmalloc = mmalloc;
-
-	address = mmap(NULL, size, PROT_WRITE|PROT_READ, MAP_ANON|MAP_SHARED,
-	    -1, 0);
-	if (address == MAP_FAILED)
-		fatal("mmap(%zu): %s", size, strerror(errno));
-
-	mm->address = address;
-	mm->size = size;
-
-	RB_INIT(&mm->rb_free);
-	RB_INIT(&mm->rb_allocated);
-
-	mm_make_entry(mm, &mm->rb_free, address, size);
-
-	return (mm);
-}
-
-/* Frees either the allocated or the free list */
-
-static void
-mm_freelist(struct mm_master *mmalloc, struct mmtree *head)
-{
-	struct mm_share *mms, *next;
-
-	for (mms = RB_ROOT(head); mms; mms = next) {
-		next = RB_NEXT(mmtree, head, mms);
-		RB_REMOVE(mmtree, head, mms);
-		if (mmalloc == NULL)
-			free(mms);
-		else
-			mm_free(mmalloc, mms);
-	}
-}
-
-/* Destroys a memory mapped area */
-
-void
-mm_destroy(struct mm_master *mm)
-{
-	mm_freelist(mm->mmalloc, &mm->rb_free);
-	mm_freelist(mm->mmalloc, &mm->rb_allocated);
-
-	if (munmap(mm->address, mm->size) == -1)
-		fatal("munmap(%p, %zu): %s", mm->address, mm->size,
-		    strerror(errno));
-	if (mm->mmalloc == NULL)
-		free(mm);
-	else
-		mm_free(mm->mmalloc, mm);
-}
-
-void *
-mm_xmalloc(struct mm_master *mm, size_t size)
-{
-	void *address;
-
-	address = mm_malloc(mm, size);
-	if (address == NULL)
-		fatal("%s: mm_malloc(%zu)", __func__, size);
-	memset(address, 0, size);
-	return (address);
-}
-
-
-/* Allocates data from a memory mapped area */
-
-void *
-mm_malloc(struct mm_master *mm, size_t size)
-{
-	struct mm_share *mms, *tmp;
-
-	if (size == 0)
-		fatal("mm_malloc: try to allocate 0 space");
-	if (size > SIZE_MAX - MM_MINSIZE + 1)
-		fatal("mm_malloc: size too big");
-
-	size = ((size + (MM_MINSIZE - 1)) / MM_MINSIZE) * MM_MINSIZE;
-
-	RB_FOREACH(mms, mmtree, &mm->rb_free) {
-		if (mms->size >= size)
-			break;
-	}
-
-	if (mms == NULL)
-		return (NULL);
-
-	/* Debug */
-	memset(mms->address, 0xd0, size);
-
-	tmp = mm_make_entry(mm, &mm->rb_allocated, mms->address, size);
-
-	/* Does not change order in RB tree */
-	mms->size -= size;
-	mms->address = (char *)mms->address + size;
-
-	if (mms->size == 0) {
-		RB_REMOVE(mmtree, &mm->rb_free, mms);
-		if (mm->mmalloc == NULL)
-			free(mms);
-		else
-			mm_free(mm->mmalloc, mms);
-	}
-
-	return (tmp->address);
-}
-
-/* Frees memory in a memory mapped area */
-
-void
-mm_free(struct mm_master *mm, void *address)
-{
-	struct mm_share *mms, *prev, tmp;
-
-	tmp.address = address;
-	mms = RB_FIND(mmtree, &mm->rb_allocated, &tmp);
-	if (mms == NULL)
-		fatal("mm_free(%p): can not find %p", mm, address);
-
-	/* Debug */
-	memset(mms->address, 0xd0, mms->size);
-
-	/* Remove from allocated list and insert in free list */
-	RB_REMOVE(mmtree, &mm->rb_allocated, mms);
-	if (RB_INSERT(mmtree, &mm->rb_free, mms) != NULL)
-		fatal("mm_free(%p): double address %p", mm, address);
-
-	/* Find previous entry */
-	prev = mms;
-	if (RB_LEFT(prev, next)) {
-		prev = RB_LEFT(prev, next);
-		while (RB_RIGHT(prev, next))
-			prev = RB_RIGHT(prev, next);
-	} else {
-		if (RB_PARENT(prev, next) &&
-		    (prev == RB_RIGHT(RB_PARENT(prev, next), next)))
-			prev = RB_PARENT(prev, next);
-		else {
-			while (RB_PARENT(prev, next) &&
-			    (prev == RB_LEFT(RB_PARENT(prev, next), next)))
-				prev = RB_PARENT(prev, next);
-			prev = RB_PARENT(prev, next);
-		}
-	}
-
-	/* Check if range does not overlap */
-	if (prev != NULL && MM_ADDRESS_END(prev) > address)
-		fatal("mm_free: memory corruption: %p(%zu) > %p",
-		    prev->address, prev->size, address);
-
-	/* See if we can merge backwards */
-	if (prev != NULL && MM_ADDRESS_END(prev) == address) {
-		prev->size += mms->size;
-		RB_REMOVE(mmtree, &mm->rb_free, mms);
-		if (mm->mmalloc == NULL)
-			free(mms);
-		else
-			mm_free(mm->mmalloc, mms);
-	} else
-		prev = mms;
-
-	if (prev == NULL)
-		return;
-
-	/* Check if we can merge forwards */
-	mms = RB_NEXT(mmtree, &mm->rb_free, prev);
-	if (mms == NULL)
-		return;
-
-	if (MM_ADDRESS_END(prev) > mms->address)
-		fatal("mm_free: memory corruption: %p < %p(%zu)",
-		    mms->address, prev->address, prev->size);
-	if (MM_ADDRESS_END(prev) != mms->address)
-		return;
-
-	prev->size += mms->size;
-	RB_REMOVE(mmtree, &mm->rb_free, mms);
-
-	if (mm->mmalloc == NULL)
-		free(mms);
-	else
-		mm_free(mm->mmalloc, mms);
-}
-
-static void
-mm_sync_list(struct mmtree *oldtree, struct mmtree *newtree,
-    struct mm_master *mm, struct mm_master *mmold)
-{
-	struct mm_master *mmalloc = mm->mmalloc;
-	struct mm_share *mms, *new;
-
-	/* Sync free list */
-	RB_FOREACH(mms, mmtree, oldtree) {
-		/* Check the values */
-		mm_memvalid(mmold, mms, sizeof(struct mm_share));
-		mm_memvalid(mm, mms->address, mms->size);
-
-		new = mm_xmalloc(mmalloc, sizeof(struct mm_share));
-		memcpy(new, mms, sizeof(struct mm_share));
-		RB_INSERT(mmtree, newtree, new);
-	}
-}
-
-void
-mm_share_sync(struct mm_master **pmm, struct mm_master **pmmalloc)
-{
-	struct mm_master *mm;
-	struct mm_master *mmalloc;
-	struct mm_master *mmold;
-	struct mmtree rb_free, rb_allocated;
-
-	debug3("%s: Share sync", __func__);
-
-	mm = *pmm;
-	mmold = mm->mmalloc;
-	mm_memvalid(mmold, mm, sizeof(*mm));
-
-	mmalloc = mm_create(NULL, mm->size);
-	mm = mm_xmalloc(mmalloc, sizeof(struct mm_master));
-	memcpy(mm, *pmm, sizeof(struct mm_master));
-	mm->mmalloc = mmalloc;
-
-	rb_free = mm->rb_free;
-	rb_allocated = mm->rb_allocated;
-
-	RB_INIT(&mm->rb_free);
-	RB_INIT(&mm->rb_allocated);
-
-	mm_sync_list(&rb_free, &mm->rb_free, mm, mmold);
-	mm_sync_list(&rb_allocated, &mm->rb_allocated, mm, mmold);
-
-	mm_destroy(mmold);
-
-	*pmm = mm;
-	*pmmalloc = mmalloc;
-
-	debug3("%s: Share sync end", __func__);
-}
-
-void
-mm_memvalid(struct mm_master *mm, void *address, size_t size)
-{
-	void *end = (char *)address + size;
-
-	if (address < mm->address)
-		fatal("mm_memvalid: address too small: %p", address);
-	if (end < address)
-		fatal("mm_memvalid: end < address: %p < %p", end, address);
-	if (end > MM_ADDRESS_END(mm))
-		fatal("mm_memvalid: address too large: %p", address);
-}
diff --git a/usr.bin/ssh/monitor_mm.h b/usr.bin/ssh/monitor_mm.h
deleted file mode 100644
index f1fae7e3bf5..00000000000
--- a/usr.bin/ssh/monitor_mm.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* $OpenBSD: monitor_mm.h,v 1.6 2014/01/04 17:50:55 tedu Exp $ */
-
-/*
- * Copyright 2002 Niels Provos <provos@citi.umich.edu>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef _MM_H_
-#define _MM_H_
-
-struct mm_share {
-	RB_ENTRY(mm_share) next;
-	void *address;
-	size_t size;
-};
-
-struct mm_master {
-	RB_HEAD(mmtree, mm_share) rb_free;
-	struct mmtree rb_allocated;
-	void *address;
-	size_t size;
-
-	struct mm_master *mmalloc;	/* Used to completely share */
-};
-
-RB_PROTOTYPE(mmtree, mm_share, next, mm_compare)
-
-#define MM_MINSIZE		128
-
-#define MM_ADDRESS_END(x)	(void *)((char *)(x)->address + (x)->size)
-
-struct mm_master *mm_create(struct mm_master *, size_t);
-void mm_destroy(struct mm_master *);
-
-void mm_share_sync(struct mm_master **, struct mm_master **);
-
-void *mm_malloc(struct mm_master *, size_t);
-void *mm_xmalloc(struct mm_master *, size_t);
-void mm_free(struct mm_master *, void *);
-
-void mm_memvalid(struct mm_master *, void *, size_t);
-#endif /* _MM_H_ */
diff --git a/usr.bin/ssh/monitor_wrap.h b/usr.bin/ssh/monitor_wrap.h
index fd84aa8fcc2..995584f4703 100644
--- a/usr.bin/ssh/monitor_wrap.h
+++ b/usr.bin/ssh/monitor_wrap.h
@@ -1,6 +1,6 @@
-/* $OpenBSD: monitor_wrap.h,v 1.31 2016/08/13 17:47:41 markus Exp $ */
+/* $OpenBSD: monitor_wrap.h,v 1.32 2016/09/28 16:33:07 djm Exp $ */
 
 /*
  * Copyright 2002 Niels Provos <provos@citi.umich.edu>
  * All rights reserved.
  *
@@ -74,9 +74,6 @@ void mm_send_keystate(struct monitor*);
 
 /* bsdauth */
 int mm_bsdauth_query(void *, char **, char **, u_int *, char ***, u_int **);
 int mm_bsdauth_respond(void *, u_int, char **);
 
-/* zlib allocation hooks */
-void mm_init_compression(struct mm_master *);
-
 #endif /* _MM_WRAP_H_ */
diff --git a/usr.bin/ssh/myproposal.h b/usr.bin/ssh/myproposal.h
index fb369174e9b..908dd463ce9 100644
--- a/usr.bin/ssh/myproposal.h
+++ b/usr.bin/ssh/myproposal.h
@@ -1,6 +1,6 @@
-/* $OpenBSD: myproposal.h,v 1.53 2016/09/22 17:52:53 djm Exp $ */
+/* $OpenBSD: myproposal.h,v 1.54 2016/09/28 16:33:07 djm Exp $ */
 
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -108,11 +108,11 @@
 #define	KEX_CLIENT_ENCRYPT KEX_SERVER_ENCRYPT
 #define KEX_CLIENT_MAC KEX_SERVER_MAC
 
 #endif /* WITH_OPENSSL */
 
-#define	KEX_DEFAULT_COMP	"none,zlib@openssh.com,zlib"
+#define	KEX_DEFAULT_COMP	"none,zlib@openssh.com"
 #define	KEX_DEFAULT_LANG	""
 
 #define KEX_CLIENT \
 	KEX_CLIENT_KEX, \
 	KEX_DEFAULT_PK_ALG, \
diff --git a/usr.bin/ssh/opacket.h b/usr.bin/ssh/opacket.h
index a1d5a777c4f..abb7b467259 100644
--- a/usr.bin/ssh/opacket.h
+++ b/usr.bin/ssh/opacket.h
@@ -130,13 +130,10 @@ void	 packet_read_expect(int expected_type);
 	ssh_packet_set_authenticated(active_state)
 #define packet_get_input() \
 	ssh_packet_get_input(active_state)
 #define packet_get_output() \
 	ssh_packet_get_output(active_state)
-#define packet_set_compress_hooks(ctx, allocfunc, freefunc) \
-	ssh_packet_set_compress_hooks(active_state, ctx, \
-	    allocfunc, freefunc);
 #define packet_check_eom() \
 	ssh_packet_check_eom(active_state)
 #define set_newkeys(mode) \
 	ssh_set_newkeys(active_state, (mode))
 #define packet_get_state(m) \
diff --git a/usr.bin/ssh/packet.c b/usr.bin/ssh/packet.c
index 9c340f5620e..36f7fdff61b 100644
--- a/usr.bin/ssh/packet.c
+++ b/usr.bin/ssh/packet.c
@@ -1,6 +1,6 @@
-/* $OpenBSD: packet.c,v 1.238 2016/09/19 19:02:19 markus Exp $ */
+/* $OpenBSD: packet.c,v 1.239 2016/09/28 16:33:07 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
  *                    All rights reserved
  * This file contains code implementing the packet protocol and communication
@@ -743,90 +743,10 @@ uncompress_buffer(struct ssh *ssh, struct sshbuf *in, struct sshbuf *out)
 		}
 	}
 	/* NOTREACHED */
 }
 
-/* Serialise compression state into a blob for privsep */
-static int
-ssh_packet_get_compress_state(struct sshbuf *m, struct ssh *ssh)
-{
-	struct session_state *state = ssh->state;
-	struct sshbuf *b;
-	int r;
-
-	if ((b = sshbuf_new()) == NULL)
-		return SSH_ERR_ALLOC_FAIL;
-	if (state->compression_in_started) {
-		if ((r = sshbuf_put_string(b, &state->compression_in_stream,
-		    sizeof(state->compression_in_stream))) != 0)
-			goto out;
-	} else if ((r = sshbuf_put_string(b, NULL, 0)) != 0)
-		goto out;
-	if (state->compression_out_started) {
-		if ((r = sshbuf_put_string(b, &state->compression_out_stream,
-		    sizeof(state->compression_out_stream))) != 0)
-			goto out;
-	} else if ((r = sshbuf_put_string(b, NULL, 0)) != 0)
-		goto out;
-	r = sshbuf_put_stringb(m, b);
- out:
-	sshbuf_free(b);
-	return r;
-}
-
-/* Deserialise compression state from a blob for privsep */
-static int
-ssh_packet_set_compress_state(struct ssh *ssh, struct sshbuf *m)
-{
-	struct session_state *state = ssh->state;
-	struct sshbuf *b = NULL;
-	int r;
-	const u_char *inblob, *outblob;
-	size_t inl, outl;
-
-	if ((r = sshbuf_froms(m, &b)) != 0)
-		goto out;
-	if ((r = sshbuf_get_string_direct(b, &inblob, &inl)) != 0 ||
-	    (r = sshbuf_get_string_direct(b, &outblob, &outl)) != 0)
-		goto out;
-	if (inl == 0)
-		state->compression_in_started = 0;
-	else if (inl != sizeof(state->compression_in_stream)) {
-		r = SSH_ERR_INTERNAL_ERROR;
-		goto out;
-	} else {
-		state->compression_in_started = 1;
-		memcpy(&state->compression_in_stream, inblob, inl);
-	}
-	if (outl == 0)
-		state->compression_out_started = 0;
-	else if (outl != sizeof(state->compression_out_stream)) {
-		r = SSH_ERR_INTERNAL_ERROR;
-		goto out;
-	} else {
-		state->compression_out_started = 1;
-		memcpy(&state->compression_out_stream, outblob, outl);
-	}
-	r = 0;
- out:
-	sshbuf_free(b);
-	return r;
-}
-
-void
-ssh_packet_set_compress_hooks(struct ssh *ssh, void *ctx,
-    void *(*allocfunc)(void *, u_int, u_int),
-    void (*freefunc)(void *, void *))
-{
-	ssh->state->compression_out_stream.zalloc = (alloc_func)allocfunc;
-	ssh->state->compression_out_stream.zfree = (free_func)freefunc;
-	ssh->state->compression_out_stream.opaque = ctx;
-	ssh->state->compression_in_stream.zalloc = (alloc_func)allocfunc;
-	ssh->state->compression_in_stream.zfree = (free_func)freefunc;
-	ssh->state->compression_in_stream.opaque = ctx;
-}
-
 /*
  * Causes any further packets to be encrypted using the given key.  The same
  * key is used for both sending and reception.  However, both directions are
  * encrypted independently of each other.
  */
@@ -2428,25 +2348,18 @@ ssh_packet_get_output(struct ssh *ssh)
 
 /* Reset after_authentication and reset compression in post-auth privsep */
 static int
 ssh_packet_set_postauth(struct ssh *ssh)
 {
-	struct sshcomp *comp;
-	int r, mode;
+	int r;
 
 	debug("%s: called", __func__);
 	/* This was set in net child, but is not visible in user child */
 	ssh->state->after_authentication = 1;
 	ssh->state->rekeying = 0;
-	for (mode = 0; mode < MODE_MAX; mode++) {
-		if (ssh->state->newkeys[mode] == NULL)
-			continue;
-		comp = &ssh->state->newkeys[mode]->comp;
-		if (comp && comp->enabled &&
-		    (r = ssh_packet_init_compression(ssh)) != 0)
-			return r;
-	}
+	if ((r = ssh_packet_enable_delayed_compress(ssh)) != 0)
+		return r;
 	return 0;
 }
 
 /* Packet state (de-)serialization for privsep */
 
@@ -2506,11 +2419,10 @@ newkeys_to_blob(struct sshbuf *m, struct ssh *ssh, int mode)
 		    (r = sshbuf_put_u32(b, mac->enabled)) != 0 ||
 		    (r = sshbuf_put_string(b, mac->key, mac->key_len)) != 0)
 			goto out;
 	}
 	if ((r = sshbuf_put_u32(b, comp->type)) != 0 ||
-	    (r = sshbuf_put_u32(b, comp->enabled)) != 0 ||
 	    (r = sshbuf_put_cstring(b, comp->name)) != 0)
 		goto out;
 	r = sshbuf_put_stringb(m, b);
  out:
 	sshbuf_free(b);
@@ -2567,13 +2479,11 @@ ssh_packet_get_state(struct ssh *ssh, struct sshbuf *m)
 	if ((r = sshbuf_put_u32(m, rlen)) != 0 ||
 	    (r = sshbuf_reserve(m, rlen, &p)) != 0)
 		return r;
 	if (cipher_get_keycontext(state->receive_context, p) != (int)rlen)
 		return SSH_ERR_INTERNAL_ERROR;
-
-	if ((r = ssh_packet_get_compress_state(m, ssh)) != 0 ||
-	    (r = sshbuf_put_stringb(m, state->input)) != 0 ||
+	if ((r = sshbuf_put_stringb(m, state->input)) != 0 ||
 	    (r = sshbuf_put_stringb(m, state->output)) != 0)
 		return r;
 
 	return 0;
 }
@@ -2623,11 +2533,10 @@ newkeys_from_blob(struct sshbuf *m, struct ssh *ssh, int mode)
 			goto out;
 		}
 		mac->key_len = maclen;
 	}
 	if ((r = sshbuf_get_u32(b, &comp->type)) != 0 ||
-	    (r = sshbuf_get_u32(b, (u_int *)&comp->enabled)) != 0 ||
 	    (r = sshbuf_get_cstring(b, &comp->name, NULL)) != 0)
 		goto out;
 	if (enc->name == NULL ||
 	    cipher_by_name(enc->name) != enc->cipher) {
 		r = SSH_ERR_INVALID_FORMAT;
@@ -2751,12 +2660,11 @@ ssh_packet_set_state(struct ssh *ssh, struct sshbuf *m)
 	    cipher_get_keycontext(state->receive_context, NULL) != (int)rlen)
 		return SSH_ERR_INVALID_FORMAT;
 	cipher_set_keycontext(state->send_context, keyout);
 	cipher_set_keycontext(state->receive_context, keyin);
 
-	if ((r = ssh_packet_set_compress_state(ssh, m)) != 0 ||
-	    (r = ssh_packet_set_postauth(ssh)) != 0)
+	if ((r = ssh_packet_set_postauth(ssh)) != 0)
 		return r;
 
 	sshbuf_reset(state->input);
 	sshbuf_reset(state->output);
 	if ((r = sshbuf_get_string_direct(m, &input, &ilen)) != 0 ||
diff --git a/usr.bin/ssh/packet.h b/usr.bin/ssh/packet.h
index 88cb832fd88..200e37a4ace 100644
--- a/usr.bin/ssh/packet.h
+++ b/usr.bin/ssh/packet.h
@@ -1,6 +1,6 @@
-/* $OpenBSD: packet.h,v 1.71 2016/03/07 19:02:43 djm Exp $ */
+/* $OpenBSD: packet.h,v 1.72 2016/09/28 16:33:07 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
  *                    All rights reserved
@@ -104,15 +104,10 @@ void     ssh_packet_disconnect(struct ssh *, const char *fmt, ...)
 void     ssh_packet_send_debug(struct ssh *, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
 
 int	 ssh_set_newkeys(struct ssh *, int mode);
 void	 ssh_packet_get_bytes(struct ssh *, u_int64_t *, u_int64_t *);
 
-typedef void *(ssh_packet_comp_alloc_func)(void *, u_int, u_int);
-typedef void (ssh_packet_comp_free_func)(void *, void *);
-void	 ssh_packet_set_compress_hooks(struct ssh *, void *,
-    ssh_packet_comp_alloc_func *, ssh_packet_comp_free_func *);
-
 int	 ssh_packet_write_poll(struct ssh *);
 int	 ssh_packet_write_wait(struct ssh *);
 int      ssh_packet_have_data_to_write(struct ssh *);
 int      ssh_packet_not_very_much_data_to_write(struct ssh *);
 
diff --git a/usr.bin/ssh/servconf.c b/usr.bin/ssh/servconf.c
index a1352f3be21..33faabfaf36 100644
--- a/usr.bin/ssh/servconf.c
+++ b/usr.bin/ssh/servconf.c
@@ -1,7 +1,7 @@
 
-/* $OpenBSD: servconf.c,v 1.295 2016/08/25 23:57:54 djm Exp $ */
+/* $OpenBSD: servconf.c,v 1.296 2016/09/28 16:33:07 djm Exp $ */
 /*
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
  *                    All rights reserved
  *
  * As far as I am concerned, the code I have written for this software
@@ -873,11 +873,11 @@ static const struct multistate multistate_permitrootlogin[] = {
 	{ "no",				PERMIT_NO },
 	{ NULL, -1 }
 };
 static const struct multistate multistate_compression[] = {
 	{ "delayed",			COMP_DELAYED },
-	{ "yes",			COMP_ZLIB },
+	{ "yes",			COMP_DELAYED },
 	{ "no",				COMP_NONE },
 	{ NULL, -1 }
 };
 static const struct multistate multistate_gatewayports[] = {
 	{ "clientspecified",		2 },
diff --git a/usr.bin/ssh/sshconnect2.c b/usr.bin/ssh/sshconnect2.c
index e7d39840e7c..f975accacdc 100644
--- a/usr.bin/ssh/sshconnect2.c
+++ b/usr.bin/ssh/sshconnect2.c
@@ -1,6 +1,6 @@
-/* $OpenBSD: sshconnect2.c,v 1.248 2016/09/22 02:29:57 dtucker Exp $ */
+/* $OpenBSD: sshconnect2.c,v 1.249 2016/09/28 16:33:07 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  * Copyright (c) 2008 Damien Miller.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -166,11 +166,11 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
 	    compat_cipher_proposal(options.ciphers);
 	myproposal[PROPOSAL_ENC_ALGS_STOC] =
 	    compat_cipher_proposal(options.ciphers);
 	myproposal[PROPOSAL_COMP_ALGS_CTOS] =
 	    myproposal[PROPOSAL_COMP_ALGS_STOC] = options.compression ?
-	    "zlib@openssh.com,zlib,none" : "none,zlib@openssh.com,zlib";
+	    "zlib@openssh.com,none" : "none,zlib@openssh.com";
 	myproposal[PROPOSAL_MAC_ALGS_CTOS] =
 	    myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs;
 	if (options.hostkeyalgorithms != NULL) {
 		if (kex_assemble_names(KEX_DEFAULT_PK_ALG,
 		    &options.hostkeyalgorithms) != 0)
diff --git a/usr.bin/ssh/sshd.c b/usr.bin/ssh/sshd.c
index a6429c8eec3..f57e61869e2 100644
--- a/usr.bin/ssh/sshd.c
+++ b/usr.bin/ssh/sshd.c
@@ -1,6 +1,6 @@
-/* $OpenBSD: sshd.c,v 1.475 2016/08/28 22:28:12 djm Exp $ */
+/* $OpenBSD: sshd.c,v 1.476 2016/09/28 16:33:07 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
  *                    All rights reserved
  * This program is the ssh daemon.  It listens for connections from clients,
@@ -94,11 +94,10 @@
 #include "authfd.h"
 #include "msg.h"
 #include "dispatch.h"
 #include "channels.h"
 #include "session.h"
-#include "monitor_mm.h"
 #include "monitor.h"
 #ifdef GSSAPI
 #include "ssh-gss.h"
 #endif
 #include "monitor_wrap.h"
@@ -557,13 +556,10 @@ privsep_preauth(Authctxt *authctxt)
 		}
 		if (box != NULL)
 			ssh_sandbox_parent_preauth(box, pid);
 		monitor_child_preauth(authctxt, pmonitor);
 
-		/* Sync memory */
-		monitor_sync(pmonitor);
-
 		/* Wait for the child's exit status */
 		while (waitpid(pid, &status, 0) < 0) {
 			if (errno == EINTR)
 				continue;
 			pmonitor->m_pid = -1;
@@ -1970,14 +1966,10 @@ do_ssh2_kex(void)
 	    myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs;
 
 	if (options.compression == COMP_NONE) {
 		myproposal[PROPOSAL_COMP_ALGS_CTOS] =
 		    myproposal[PROPOSAL_COMP_ALGS_STOC] = "none";
-	} else if (options.compression == COMP_DELAYED) {
-		myproposal[PROPOSAL_COMP_ALGS_CTOS] =
-		    myproposal[PROPOSAL_COMP_ALGS_STOC] =
-		    "none,zlib@openssh.com";
 	}
 
 	if (options.rekey_limit || options.rekey_interval)
 		packet_set_rekey_limits(options.rekey_limit,
 		    (time_t)options.rekey_interval);
diff --git a/usr.bin/ssh/sshd/Makefile b/usr.bin/ssh/sshd/Makefile
index eaea9460901..979c0618001 100644
--- a/usr.bin/ssh/sshd/Makefile
+++ b/usr.bin/ssh/sshd/Makefile
@@ -1,6 +1,6 @@
-#	$OpenBSD: Makefile,v 1.89 2016/08/13 17:47:41 markus Exp $
+#	$OpenBSD: Makefile,v 1.90 2016/09/28 16:33:07 djm Exp $
 
 .PATH:		${.CURDIR}/..
 .include "${.CURDIR}/../Makefile.inc"
 
 PROG=	sshd
@@ -13,11 +13,11 @@ SRCS=	sshd.c auth-rhosts.c auth-passwd.c \
 	sshpty.c sshlogin.c servconf.c serverloop.c \
 	auth.c auth2.c auth-options.c session.c \
 	auth2-chall.c groupaccess.c \
 	auth-bsdauth.c auth2-hostbased.c auth2-kbdint.c \
 	auth2-none.c auth2-passwd.c auth2-pubkey.c \
-	monitor_mm.c monitor.c monitor_wrap.c \
+	monitor.c monitor_wrap.c \
 	sftp-server.c sftp-common.c \
 	sandbox-pledge.c
 
 .include <bsd.own.mk> # for KERBEROS and AFS
 

Vulnerable code: /* $OpenBSD: monitor.c,v 1.165 2016/09/05 13:57:31 djm Exp $ */
#include "monitor_mm.h"
void
monitor_sync(struct monitor *pmonitor)
{
	if (options.compression) {
		/* The member allocation is not visible, so sync it */
		mm_share_sync(&pmonitor->m_zlib, &pmonitor->m_zback);
	}
}

/* Allocation functions for zlib */
static void *
mm_zalloc(struct mm_master *mm, u_int ncount, u_int size)
{
	if (size == 0 || ncount == 0 || ncount > SIZE_MAX / size)
		fatal("%s: mm_zalloc(%u, %u)", __func__, ncount, size);

	return mm_malloc(mm, size * ncount);
}

static void
mm_zfree(struct mm_master *mm, void *address)
{
	mm_free(mm, address);
}


	/* Update with new address */
	if (options.compression) {
		ssh_packet_set_compress_hooks(ssh, pmonitor->m_zlib,
		    (ssh_packet_comp_alloc_func *)mm_zalloc,
		    (ssh_packet_comp_free_func *)mm_zfree);
	}
	struct ssh *ssh = active_state;			/* XXX */

	/* Used to share zlib space across processes */
	if (options.compression) {
		mon->m_zback = mm_create(NULL, MM_MEMSIZE);
		mon->m_zlib = mm_create(mon->m_zback, 20 * MM_MEMSIZE);

		/* Compression needs to share state across borders */
		ssh_packet_set_compress_hooks(ssh, mon->m_zlib,
		    (ssh_packet_comp_alloc_func *)mm_zalloc,
		    (ssh_packet_comp_free_func *)mm_zfree);
	}

/* $OpenBSD: monitor.h,v 1.19 2015/01/19 19:52:16 markus Exp $ */
struct mm_master;
	struct mm_master	*m_zback;
	struct mm_master	*m_zlib;
void monitor_sync(struct monitor *);
/* $OpenBSD: monitor_mm.c,v 1.21 2015/02/06 23:21:59 millert Exp $ */
/*
 * Copyright 2002 Niels Provos <provos@citi.umich.edu>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/types.h>
#include <sys/mman.h>
#include <sys/tree.h>

#include <errno.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include "xmalloc.h"
#include "ssh.h"
#include "log.h"
#include "monitor_mm.h"

static int
mm_compare(struct mm_share *a, struct mm_share *b)
{
	ptrdiff_t diff = (char *)a->address - (char *)b->address;

	if (diff == 0)
		return (0);
	else if (diff < 0)
		return (-1);
	else
		return (1);
}

RB_GENERATE(mmtree, mm_share, next, mm_compare)

static struct mm_share *
mm_make_entry(struct mm_master *mm, struct mmtree *head,
    void *address, size_t size)
{
	struct mm_share *tmp, *tmp2;

	if (mm->mmalloc == NULL)
		tmp = xcalloc(1, sizeof(struct mm_share));
	else
		tmp = mm_xmalloc(mm->mmalloc, sizeof(struct mm_share));
	tmp->address = address;
	tmp->size = size;

	tmp2 = RB_INSERT(mmtree, head, tmp);
	if (tmp2 != NULL)
		fatal("mm_make_entry(%p): double address %p->%p(%zu)",
		    mm, tmp2, address, size);

	return (tmp);
}

/* Creates a shared memory area of a certain size */

struct mm_master *
mm_create(struct mm_master *mmalloc, size_t size)
{
	void *address;
	struct mm_master *mm;

	if (mmalloc == NULL)
		mm = xcalloc(1, sizeof(struct mm_master));
	else
		mm = mm_xmalloc(mmalloc, sizeof(struct mm_master));

	/*
	 * If the memory map has a mm_master it can be completely
	 * shared including authentication between the child
	 * and the client.
	 */
	mm->mmalloc = mmalloc;

	address = mmap(NULL, size, PROT_WRITE|PROT_READ, MAP_ANON|MAP_SHARED,
	    -1, 0);
	if (address == MAP_FAILED)
		fatal("mmap(%zu): %s", size, strerror(errno));

	mm->address = address;
	mm->size = size;

	RB_INIT(&mm->rb_free);
	RB_INIT(&mm->rb_allocated);

	mm_make_entry(mm, &mm->rb_free, address, size);

	return (mm);
}

/* Frees either the allocated or the free list */

static void
mm_freelist(struct mm_master *mmalloc, struct mmtree *head)
{
	struct mm_share *mms, *next;

	for (mms = RB_ROOT(head); mms; mms = next) {
		next = RB_NEXT(mmtree, head, mms);
		RB_REMOVE(mmtree, head, mms);
		if (mmalloc == NULL)
			free(mms);
		else
			mm_free(mmalloc, mms);
	}
}

/* Destroys a memory mapped area */

void
mm_destroy(struct mm_master *mm)
{
	mm_freelist(mm->mmalloc, &mm->rb_free);
	mm_freelist(mm->mmalloc, &mm->rb_allocated);

	if (munmap(mm->address, mm->size) == -1)
		fatal("munmap(%p, %zu): %s", mm->address, mm->size,
		    strerror(errno));
	if (mm->mmalloc == NULL)
		free(mm);
	else
		mm_free(mm->mmalloc, mm);
}

void *
mm_xmalloc(struct mm_master *mm, size_t size)
{
	void *address;

	address = mm_malloc(mm, size);
	if (address == NULL)
		fatal("%s: mm_malloc(%zu)", __func__, size);
	memset(address, 0, size);
	return (address);
}


/* Allocates data from a memory mapped area */

void *
mm_malloc(struct mm_master *mm, size_t size)
{
	struct mm_share *mms, *tmp;

	if (size == 0)
		fatal("mm_malloc: try to allocate 0 space");
	if (size > SIZE_MAX - MM_MINSIZE + 1)
		fatal("mm_malloc: size too big");

	size = ((size + (MM_MINSIZE - 1)) / MM_MINSIZE) * MM_MINSIZE;

	RB_FOREACH(mms, mmtree, &mm->rb_free) {
		if (mms->size >= size)
			break;
	}

	if (mms == NULL)
		return (NULL);

	/* Debug */
	memset(mms->address, 0xd0, size);

	tmp = mm_make_entry(mm, &mm->rb_allocated, mms->address, size);

	/* Does not change order in RB tree */
	mms->size -= size;
	mms->address = (char *)mms->address + size;

	if (mms->size == 0) {
		RB_REMOVE(mmtree, &mm->rb_free, mms);
		if (mm->mmalloc == NULL)
			free(mms);
		else
			mm_free(mm->mmalloc, mms);
	}

	return (tmp->address);
}

/* Frees memory in a memory mapped area */

void
mm_free(struct mm_master *mm, void *address)
{
	struct mm_share *mms, *prev, tmp;

	tmp.address = address;
	mms = RB_FIND(mmtree, &mm->rb_allocated, &tmp);
	if (mms == NULL)
		fatal("mm_free(%p): can not find %p", mm, address);

	/* Debug */
	memset(mms->address, 0xd0, mms->size);

	/* Remove from allocated list and insert in free list */
	RB_REMOVE(mmtree, &mm->rb_allocated, mms);
	if (RB_INSERT(mmtree, &mm->rb_free, mms) != NULL)
		fatal("mm_free(%p): double address %p", mm, address);

	/* Find previous entry */
	prev = mms;
	if (RB_LEFT(prev, next)) {
		prev = RB_LEFT(prev, next);
		while (RB_RIGHT(prev, next))
			prev = RB_RIGHT(prev, next);
	} else {
		if (RB_PARENT(prev, next) &&
		    (prev == RB_RIGHT(RB_PARENT(prev, next), next)))
			prev = RB_PARENT(prev, next);
		else {
			while (RB_PARENT(prev, next) &&
			    (prev == RB_LEFT(RB_PARENT(prev, next), next)))
				prev = RB_PARENT(prev, next);
			prev = RB_PARENT(prev, next);
		}
	}

	/* Check if range does not overlap */
	if (prev != NULL && MM_ADDRESS_END(prev) > address)
		fatal("mm_free: memory corruption: %p(%zu) > %p",
		    prev->address, prev->size, address);

	/* See if we can merge backwards */
	if (prev != NULL && MM_ADDRESS_END(prev) == address) {
		prev->size += mms->size;
		RB_REMOVE(mmtree, &mm->rb_free, mms);
		if (mm->mmalloc == NULL)
			free(mms);
		else
			mm_free(mm->mmalloc, mms);
	} else
		prev = mms;

	if (prev == NULL)
		return;

	/* Check if we can merge forwards */
	mms = RB_NEXT(mmtree, &mm->rb_free, prev);
	if (mms == NULL)
		return;

	if (MM_ADDRESS_END(prev) > mms->address)
		fatal("mm_free: memory corruption: %p < %p(%zu)",
		    mms->address, prev->address, prev->size);
	if (MM_ADDRESS_END(prev) != mms->address)
		return;

	prev->size += mms->size;
	RB_REMOVE(mmtree, &mm->rb_free, mms);

	if (mm->mmalloc == NULL)
		free(mms);
	else
		mm_free(mm->mmalloc, mms);
}

static void
mm_sync_list(struct mmtree *oldtree, struct mmtree *newtree,
    struct mm_master *mm, struct mm_master *mmold)
{
	struct mm_master *mmalloc = mm->mmalloc;
	struct mm_share *mms, *new;

	/* Sync free list */
	RB_FOREACH(mms, mmtree, oldtree) {
		/* Check the values */
		mm_memvalid(mmold, mms, sizeof(struct mm_share));
		mm_memvalid(mm, mms->address, mms->size);

		new = mm_xmalloc(mmalloc, sizeof(struct mm_share));
		memcpy(new, mms, sizeof(struct mm_share));
		RB_INSERT(mmtree, newtree, new);
	}
}

void
mm_share_sync(struct mm_master **pmm, struct mm_master **pmmalloc)
{
	struct mm_master *mm;
	struct mm_master *mmalloc;
	struct mm_master *mmold;
	struct mmtree rb_free, rb_allocated;

	debug3("%s: Share sync", __func__);

	mm = *pmm;
	mmold = mm->mmalloc;
	mm_memvalid(mmold, mm, sizeof(*mm));

	mmalloc = mm_create(NULL, mm->size);
	mm = mm_xmalloc(mmalloc, sizeof(struct mm_master));
	memcpy(mm, *pmm, sizeof(struct mm_master));
	mm->mmalloc = mmalloc;

	rb_free = mm->rb_free;
	rb_allocated = mm->rb_allocated;

	RB_INIT(&mm->rb_free);
	RB_INIT(&mm->rb_allocated);

	mm_sync_list(&rb_free, &mm->rb_free, mm, mmold);
	mm_sync_list(&rb_allocated, &mm->rb_allocated, mm, mmold);

	mm_destroy(mmold);

	*pmm = mm;
	*pmmalloc = mmalloc;

	debug3("%s: Share sync end", __func__);
}

void
mm_memvalid(struct mm_master *mm, void *address, size_t size)
{
	void *end = (char *)address + size;

	if (address < mm->address)
		fatal("mm_memvalid: address too small: %p", address);
	if (end < address)
		fatal("mm_memvalid: end < address: %p < %p", end, address);
	if (end > MM_ADDRESS_END(mm))
		fatal("mm_memvalid: address too large: %p", address);
}
/* $OpenBSD: monitor_mm.h,v 1.6 2014/01/04 17:50:55 tedu Exp $ */

/*
 * Copyright 2002 Niels Provos <provos@citi.umich.edu>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _MM_H_
#define _MM_H_

struct mm_share {
	RB_ENTRY(mm_share) next;
	void *address;
	size_t size;
};

struct mm_master {
	RB_HEAD(mmtree, mm_share) rb_free;
	struct mmtree rb_allocated;
	void *address;
	size_t size;

	struct mm_master *mmalloc;	/* Used to completely share */
};

RB_PROTOTYPE(mmtree, mm_share, next, mm_compare)

#define MM_MINSIZE		128

#define MM_ADDRESS_END(x)	(void *)((char *)(x)->address + (x)->size)

struct mm_master *mm_create(struct mm_master *, size_t);
void mm_destroy(struct mm_master *);

void mm_share_sync(struct mm_master **, struct mm_master **);

void *mm_malloc(struct mm_master *, size_t);
void *mm_xmalloc(struct mm_master *, size_t);
void mm_free(struct mm_master *, void *);

void mm_memvalid(struct mm_master *, void *, size_t);
#endif /* _MM_H_ */
/* $OpenBSD: monitor_wrap.h,v 1.31 2016/08/13 17:47:41 markus Exp $ */
/* zlib allocation hooks */
void mm_init_compression(struct mm_master *);

/* $OpenBSD: myproposal.h,v 1.53 2016/09/22 17:52:53 djm Exp $ */
#define	KEX_DEFAULT_COMP	"none,zlib@openssh.com,zlib"
#define packet_set_compress_hooks(ctx, allocfunc, freefunc) \
	ssh_packet_set_compress_hooks(active_state, ctx, \
	    allocfunc, freefunc);
/* $OpenBSD: packet.c,v 1.238 2016/09/19 19:02:19 markus Exp $ */
/* Serialise compression state into a blob for privsep */
static int
ssh_packet_get_compress_state(struct sshbuf *m, struct ssh *ssh)
{
	struct session_state *state = ssh->state;
	struct sshbuf *b;
	int r;

	if ((b = sshbuf_new()) == NULL)
		return SSH_ERR_ALLOC_FAIL;
	if (state->compression_in_started) {
		if ((r = sshbuf_put_string(b, &state->compression_in_stream,
		    sizeof(state->compression_in_stream))) != 0)
			goto out;
	} else if ((r = sshbuf_put_string(b, NULL, 0)) != 0)
		goto out;
	if (state->compression_out_started) {
		if ((r = sshbuf_put_string(b, &state->compression_out_stream,
		    sizeof(state->compression_out_stream))) != 0)
			goto out;
	} else if ((r = sshbuf_put_string(b, NULL, 0)) != 0)
		goto out;
	r = sshbuf_put_stringb(m, b);
 out:
	sshbuf_free(b);
	return r;
}

/* Deserialise compression state from a blob for privsep */
static int
ssh_packet_set_compress_state(struct ssh *ssh, struct sshbuf *m)
{
	struct session_state *state = ssh->state;
	struct sshbuf *b = NULL;
	int r;
	const u_char *inblob, *outblob;
	size_t inl, outl;

	if ((r = sshbuf_froms(m, &b)) != 0)
		goto out;
	if ((r = sshbuf_get_string_direct(b, &inblob, &inl)) != 0 ||
	    (r = sshbuf_get_string_direct(b, &outblob, &outl)) != 0)
		goto out;
	if (inl == 0)
		state->compression_in_started = 0;
	else if (inl != sizeof(state->compression_in_stream)) {
		r = SSH_ERR_INTERNAL_ERROR;
		goto out;
	} else {
		state->compression_in_started = 1;
		memcpy(&state->compression_in_stream, inblob, inl);
	}
	if (outl == 0)
		state->compression_out_started = 0;
	else if (outl != sizeof(state->compression_out_stream)) {
		r = SSH_ERR_INTERNAL_ERROR;
		goto out;
	} else {
		state->compression_out_started = 1;
		memcpy(&state->compression_out_stream, outblob, outl);
	}
	r = 0;
 out:
	sshbuf_free(b);
	return r;
}

void
ssh_packet_set_compress_hooks(struct ssh *ssh, void *ctx,
    void *(*allocfunc)(void *, u_int, u_int),
    void (*freefunc)(void *, void *))
{
	ssh->state->compression_out_stream.zalloc = (alloc_func)allocfunc;
	ssh->state->compression_out_stream.zfree = (free_func)freefunc;
	ssh->state->compression_out_stream.opaque = ctx;
	ssh->state->compression_in_stream.zalloc = (alloc_func)allocfunc;
	ssh->state->compression_in_stream.zfree = (free_func)freefunc;
	ssh->state->compression_in_stream.opaque = ctx;
}

	struct sshcomp *comp;
	int r, mode;
	for (mode = 0; mode < MODE_MAX; mode++) {
		if (ssh->state->newkeys[mode] == NULL)
			continue;
		comp = &ssh->state->newkeys[mode]->comp;
		if (comp && comp->enabled &&
		    (r = ssh_packet_init_compression(ssh)) != 0)
			return r;
	}
	    (r = sshbuf_put_u32(b, comp->enabled)) != 0 ||

	if ((r = ssh_packet_get_compress_state(m, ssh)) != 0 ||
	    (r = sshbuf_put_stringb(m, state->input)) != 0 ||
	    (r = sshbuf_get_u32(b, (u_int *)&comp->enabled)) != 0 ||
	if ((r = ssh_packet_set_compress_state(ssh, m)) != 0 ||
	    (r = ssh_packet_set_postauth(ssh)) != 0)
/* $OpenBSD: packet.h,v 1.71 2016/03/07 19:02:43 djm Exp $ */
typedef void *(ssh_packet_comp_alloc_func)(void *, u_int, u_int);
typedef void (ssh_packet_comp_free_func)(void *, void *);
void	 ssh_packet_set_compress_hooks(struct ssh *, void *,
    ssh_packet_comp_alloc_func *, ssh_packet_comp_free_func *);

/* $OpenBSD: servconf.c,v 1.295 2016/08/25 23:57:54 djm Exp $ */
	{ "yes",			COMP_ZLIB },
/* $OpenBSD: sshconnect2.c,v 1.248 2016/09/22 02:29:57 dtucker Exp $ */
	    "zlib@openssh.com,zlib,none" : "none,zlib@openssh.com,zlib";
/* $OpenBSD: sshd.c,v 1.475 2016/08/28 22:28:12 djm Exp $ */
#include "monitor_mm.h"
		/* Sync memory */
		monitor_sync(pmonitor);

	} else if (options.compression == COMP_DELAYED) {
		myproposal[PROPOSAL_COMP_ALGS_CTOS] =
		    myproposal[PROPOSAL_COMP_ALGS_STOC] =
		    "none,zlib@openssh.com";
#	$OpenBSD: Makefile,v 1.89 2016/08/13 17:47:41 markus Exp $
	monitor_mm.c monitor.c monitor_wrap.c \
Fixed code: /* $OpenBSD: monitor.c,v 1.166 2016/09/28 16:33:06 djm Exp $ */
/* $OpenBSD: monitor.h,v 1.20 2016/09/28 16:33:07 djm Exp $ */
/* $OpenBSD: monitor_wrap.h,v 1.32 2016/09/28 16:33:07 djm Exp $ */
/* $OpenBSD: myproposal.h,v 1.54 2016/09/28 16:33:07 djm Exp $ */
#define	KEX_DEFAULT_COMP	"none,zlib@openssh.com"
/* $OpenBSD: packet.c,v 1.239 2016/09/28 16:33:07 djm Exp $ */
	int r;
	if ((r = ssh_packet_enable_delayed_compress(ssh)) != 0)
		return r;
	if ((r = sshbuf_put_stringb(m, state->input)) != 0 ||
	if ((r = ssh_packet_set_postauth(ssh)) != 0)
/* $OpenBSD: packet.h,v 1.72 2016/09/28 16:33:07 djm Exp $ */
/* $OpenBSD: servconf.c,v 1.296 2016/09/28 16:33:07 djm Exp $ */
	{ "yes",			COMP_DELAYED },
/* $OpenBSD: sshconnect2.c,v 1.249 2016/09/28 16:33:07 djm Exp $ */
	    "zlib@openssh.com,none" : "none,zlib@openssh.com";
/* $OpenBSD: sshd.c,v 1.476 2016/09/28 16:33:07 djm Exp $ */
#	$OpenBSD: Makefile,v 1.90 2016/09/28 16:33:07 djm Exp $
	monitor.c monitor_wrap.c \