From 21bed0d21aa765c7796d977fcb36902399e97242 Mon Sep 17 00:00:00 2001
From: Matt Johnston <matt@ucc.asn.au>
Date: Tue, 24 Feb 2015 22:01:33 +0800
Subject: [PATCH] Free memory before exiting. Based on patch from Thorsten
 Horstmann. Client side is not complete.

---
 common-session.c  | 45 +++++++++++++++++++++++++++++++++------------
 listener.c        |  9 +++++++++
 listener.h        |  2 ++
 svr-chansession.c |  4 ++++
 svr-session.c     | 17 +++++++++++++++--
 sysoptions.h      |  3 +++
 6 files changed, 66 insertions(+), 14 deletions(-)

diff --git a/common-session.c b/common-session.c
index d820d64e..a225b216 100644
--- a/common-session.c
+++ b/common-session.c
@@ -240,6 +240,15 @@ void session_loop(void(*loophandler)()) {
 	/* Not reached */
 }
 
+static void cleanup_buf(buffer **buf) {
+	if (!*buf) {
+		return;
+	}
+	buf_burn(*buf);
+	buf_free(*buf);
+	*buf = NULL;
+}
+
 /* clean up a session on exit */
 void session_cleanup() {
 	
@@ -256,19 +265,31 @@ void session_cleanup() {
 	}
 
 	chancleanup();
-	
-	/* Cleaning up keys must happen after other cleanup
-	functions which might queue packets */
-	if (ses.session_id) {
-		buf_burn(ses.session_id);
-		buf_free(ses.session_id);
-		ses.session_id = NULL;
-	}
-	if (ses.hash) {
-		buf_burn(ses.hash);
-		buf_free(ses.hash);
-		ses.hash = NULL;
+
+	/* Most dropbear functions are unsafe to run after this point */
+#ifdef DROPBEAR_CLEANUP
+	/* listeners call cleanup functions, this should occur before
+	other session state is freed. */
+	remove_all_listeners();
+
+	while (!isempty(&ses.writequeue)) {
+		buf_free(dequeue(&ses.writequeue));
 	}
+
+	m_free(ses.remoteident);
+	m_free(ses.authstate.pw_dir);
+	m_free(ses.authstate.pw_name);
+	m_free(ses.authstate.pw_shell);
+	m_free(ses.authstate.pw_passwd);
+	m_free(ses.authstate.username);
+#endif
+
+	cleanup_buf(&ses.session_id);
+	cleanup_buf(&ses.hash);
+	cleanup_buf(&ses.payload);
+	cleanup_buf(&ses.readbuf);
+	cleanup_buf(&ses.writepayload);
+
 	m_burn(ses.keys, sizeof(struct key_context));
 	m_free(ses.keys);
 
diff --git a/listener.c b/listener.c
index dd90c6bf..a7f07304 100644
--- a/listener.c
+++ b/listener.c
@@ -161,5 +161,14 @@ void remove_listener(struct Listener* listener) {
 	}
 	ses.listeners[listener->index] = NULL;
 	m_free(listener);
+}
 
+void remove_all_listeners(void) {
+	unsigned int i;
+	for (i = 0; i < ses.listensize; i++) {
+		if (ses.listeners[i]) {
+			remove_listener(ses.listeners[i]);
+		}
+	}
+	m_free(ses.listeners);
 }
diff --git a/listener.h b/listener.h
index 317665f5..b531ed6d 100644
--- a/listener.h
+++ b/listener.h
@@ -60,4 +60,6 @@ struct Listener * get_listener(int type, void* typedata,
 
 void remove_listener(struct Listener* listener);
 
+void remove_all_listeners(void);
+
 #endif /* DROPBEAR_LISTENER_H */
diff --git a/svr-chansession.c b/svr-chansession.c
index 5bed8fc7..5f9f56d5 100644
--- a/svr-chansession.c
+++ b/svr-chansession.c
@@ -787,9 +787,11 @@ static int ptycommand(struct Channel *channel, struct ChanSess *chansess) {
 		
 		TRACE(("back to normal sigchld"))
 		/* Revert to normal sigchld handling */
+		/*
 		if (signal(SIGCHLD, SIG_DFL) == SIG_ERR) {
 			dropbear_exit("signal() error");
 		}
+		*/
 		
 		/* redirect stdin/stdout/stderr */
 		close(chansess->master);
@@ -1005,9 +1007,11 @@ void svr_chansessinitialise() {
 	sa_chld.sa_handler = sesssigchild_handler;
 	sa_chld.sa_flags = SA_NOCLDSTOP;
 	sigemptyset(&sa_chld.sa_mask);
+	/*
 	if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) {
 		dropbear_exit("signal() error");
 	}
+	*/
 	
 }
 
diff --git a/svr-session.c b/svr-session.c
index 343cb30c..2b8a9560 100644
--- a/svr-session.c
+++ b/svr-session.c
@@ -78,10 +78,13 @@ static const struct ChanType *svr_chantypes[] = {
 };
 
 static void
-svr_session_cleanup(void)
-{
+svr_session_cleanup(void) {
 	/* free potential public key options */
 	svr_pubkey_options_cleanup();
+
+	m_free(svr_ses.addrstring);
+	m_free(svr_ses.childpids);
+	m_free(svr_ses.remotehost);
 }
 
 static void
@@ -150,6 +153,7 @@ void svr_session(int sock, int childpipe) {
 void svr_dropbear_exit(int exitcode, const char* format, va_list param) {
 
 	char fmtbuf[300];
+	int i;
 
 	if (!sessinitdone) {
 		/* before session init */
@@ -183,6 +187,15 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param) {
 		session_cleanup();
 	}
 
+	if (svr_opts.hostkey) {
+		sign_key_free(svr_opts.hostkey);
+		svr_opts.hostkey = NULL;
+	}
+	for (i = 0; i < DROPBEAR_MAX_PORTS; i++) {
+		m_free(svr_opts.addresses[i]);
+		m_free(svr_opts.ports[i]);
+	}
+
 	exit(exitcode);
 
 }
diff --git a/sysoptions.h b/sysoptions.h
index bec72461..03f4076e 100644
--- a/sysoptions.h
+++ b/sysoptions.h
@@ -256,6 +256,9 @@
 #define DROPBEAR_LISTEN_BACKLOG MAX_CHANNELS
 #endif
 
+/* free memory before exiting */
+#define DROPBEAR_CLEANUP
+
 /* Use this string since some implementations might special-case it */
 #define DROPBEAR_KEEPALIVE_STRING "keepalive@openssh.com"
 
-- 
GitLab