# HG changeset patch
# Parent  6851d6d75c28b20e31c5ba22eb8571dee6c4e1ed
Block OOM kills in the listener sshd process
Original backport based on openssh-5.7

bnc#719132

Additional OOM score adjustment on sshd restart

bsc#914309

diff --git a/openssh-5.1p1/configure.ac b/openssh-5.1p1/configure.ac
--- a/openssh-5.1p1/configure.ac
+++ b/openssh-5.1p1/configure.ac
@@ -570,16 +570,17 @@ main() { if (NSVersionOfRunTimeLibrary("
 	AC_DEFINE(LOCKED_PASSWD_PREFIX, "!",
 		[String used in /etc/passwd to denote locked account])
 	AC_DEFINE(SPT_TYPE,SPT_REUSEARGV)
 	AC_DEFINE(LINK_OPNOTSUPP_ERRNO, EPERM,
 		[Define to whatever link() returns for "not supported"
 		if it doesn't return EOPNOTSUPP.])
 	AC_DEFINE(_PATH_BTMP, "/var/log/btmp", [log for bad login attempts])
 	AC_DEFINE(USE_BTMP)
+	AC_DEFINE(LINUX_OOM_ADJUST, 1, [Adjust Linux out-of-memory killer])
 	inet6_default_4in6=yes
 	case `uname -r` in
 	1.*|2.0.*)
 		AC_DEFINE(BROKEN_CMSG_TYPE, 1,
 			[Define if cmsg_type is not passed correctly])
 		;;
 	esac
 	# tun(4) forwarding compat code
diff --git a/openssh-5.1p1/openbsd-compat/port-linux.c b/openssh-5.1p1/openbsd-compat/port-linux.c
--- a/openssh-5.1p1/openbsd-compat/port-linux.c
+++ b/openssh-5.1p1/openbsd-compat/port-linux.c
@@ -22,18 +22,25 @@
  */
 
 #include "includes.h"
 
 #include <errno.h>
 #include <stdarg.h>
 #include <string.h>
 
+#if defined(LINUX_OOM_ADJUST) || defined(WITH_SELINUX)
+#include "log.h"
+#endif
+
+#ifdef LINUX_OOM_ADJUST
+#include <stdio.h>
+#endif
+
 #ifdef WITH_SELINUX
-#include "log.h"
 #include "port-linux.h"
 
 #include <selinux/selinux.h>
 #include <selinux/flask.h>
 #include <selinux/get_context_list.h>
 
 /* Wrapper around is_selinux_enabled() to log its return value once only */
 int
@@ -164,8 +171,81 @@ ssh_selinux_setup_pty(char *pwname, cons
 		freecon(new_tty_ctx);
 	if (old_tty_ctx != NULL)
 		freecon(old_tty_ctx);
 	if (user_ctx != NULL)
 		freecon(user_ctx);
 	debug3("%s: done", __func__);
 }
 #endif /* WITH_SELINUX */
+
+#ifdef LINUX_OOM_ADJUST
+/*
+ * The magic "don't kill me" values, old and new, as documented in eg:
+ * http://lxr.linux.no/#linux+v2.6.32/Documentation/filesystems/proc.txt
+ * http://lxr.linux.no/#linux+v2.6.36/Documentation/filesystems/proc.txt
+ */
+
+static int oom_adj_save = INT_MIN;
+static char *oom_adj_path = NULL;
+struct {
+	char *path;
+	int value;
+} oom_adjust[] = {
+	{"/proc/self/oom_score_adj", -1000},	/* kernels >= 2.6.36 */
+	{"/proc/self/oom_adj", -17},		/* kernels <= 2.6.35 */
+	{NULL, 0},
+};
+
+/*
+ * Tell the kernel's out-of-memory killer to avoid sshd.
+ * Returns the previous oom_adj value or zero.
+ */
+void
+oom_adjust_setup(void)
+{
+	int i, value;
+	FILE *fp;
+
+	debug3("%s", __func__);
+	 for (i = 0; oom_adjust[i].path != NULL; i++) {
+		oom_adj_path = oom_adjust[i].path;
+		value = oom_adjust[i].value;
+		if ((fp = fopen(oom_adj_path, "r+")) != NULL) {
+			if (fscanf(fp, "%d", &oom_adj_save) != 1)
+				verbose("error reading %s: %s", oom_adj_path,
+				    strerror(errno));
+			else {
+				rewind(fp);
+				if (fprintf(fp, "%d\n", value) <= 0)
+					verbose("error writing %s: %s",
+					   oom_adj_path, strerror(errno));
+				else
+					verbose("Set %s from %d to %d",
+					   oom_adj_path, oom_adj_save, value);
+			}
+			fclose(fp);
+			return;
+		}
+	}
+	oom_adj_path = NULL;
+}
+
+/* Restore the saved OOM adjustment */
+void
+oom_adjust_restore(void)
+{
+	FILE *fp;
+
+	debug3("%s", __func__);
+	if (oom_adj_save == INT_MIN || oom_adj_path == NULL ||
+	    (fp = fopen(oom_adj_path, "w")) == NULL)
+		return;
+
+	if (fprintf(fp, "%d\n", oom_adj_save) <= 0)
+		verbose("error writing %s: %s", oom_adj_path, strerror(errno));
+	else
+		verbose("Set %s to %d", oom_adj_path, oom_adj_save);
+
+	fclose(fp);
+	return;
+}
+#endif
diff --git a/openssh-5.1p1/openbsd-compat/port-linux.h b/openssh-5.1p1/openbsd-compat/port-linux.h
--- a/openssh-5.1p1/openbsd-compat/port-linux.h
+++ b/openssh-5.1p1/openbsd-compat/port-linux.h
@@ -20,9 +20,14 @@
 #define _PORT_LINUX_H
 
 #ifdef WITH_SELINUX
 int ssh_selinux_enabled(void);
 void ssh_selinux_setup_pty(char *, const char *);
 void ssh_selinux_setup_exec_context(char *);
 #endif
 
+#ifdef LINUX_OOM_ADJUST
+void oom_adjust_restore(void);
+void oom_adjust_setup(void);
+#endif
+
 #endif /* ! _PORT_LINUX_H */
diff --git a/openssh-5.1p1/platform.c b/openssh-5.1p1/platform.c
--- a/openssh-5.1p1/platform.c
+++ b/openssh-5.1p1/platform.c
@@ -17,16 +17,25 @@
  */
 
 #include "config.h"
 #include "platform.h"
 
 #include "openbsd-compat/openbsd-compat.h"
 
 void
+platform_pre_listen(void)
+{
+#ifdef LINUX_OOM_ADJUST
+	/* Adjust out-of-memory killer so listening process is not killed */
+	oom_adjust_setup();
+#endif
+}
+
+void
 platform_pre_fork(void)
 {
 #ifdef USE_SOLARIS_PROCESS_CONTRACTS
 	solaris_contract_pre_fork();
 #endif
 }
 
 void
@@ -38,9 +47,12 @@ platform_post_fork_parent(pid_t child_pi
 }
 
 void
 platform_post_fork_child(void)
 {
 #ifdef USE_SOLARIS_PROCESS_CONTRACTS
 	solaris_contract_post_fork_child();
 #endif
+#ifdef LINUX_OOM_ADJUST
+	oom_adjust_restore();
+#endif
 }
diff --git a/openssh-5.1p1/platform.h b/openssh-5.1p1/platform.h
--- a/openssh-5.1p1/platform.h
+++ b/openssh-5.1p1/platform.h
@@ -13,11 +13,12 @@
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 #include <sys/types.h>
 
+void platform_pre_listen(void);
 void platform_pre_fork(void);
 void platform_post_fork_parent(pid_t child_pid);
 void platform_post_fork_child(void);
diff --git a/openssh-5.1p1/sshd.c b/openssh-5.1p1/sshd.c
--- a/openssh-5.1p1/sshd.c
+++ b/openssh-5.1p1/sshd.c
@@ -307,16 +307,19 @@ sighup_handler(int sig)
 static void
 sighup_restart(void)
 {
 	int i;
 	logit("Received SIGHUP; restarting.");
 	close_listen_socks();
 	close_startup_pipes();
 	alarm(0);  /* alarm timer persists across exec */
+#ifdef LINUX_OOM_ADJUST
+	oom_adjust_restore();
+#endif
 	execv(saved_argv[0], saved_argv);
 	logit("RESTART FAILED: av[0]='%.100s', error: %.100s.", saved_argv[0],
 	    strerror(errno));
 	exit(1);
 }
 
 /*
  * Generic signal handler for terminating signals in the master daemon.
@@ -1669,16 +1672,17 @@ main(int ac, char **av)
 
 	/* ignore SIGPIPE */
 	signal(SIGPIPE, SIG_IGN);
 
 	/* Get a connection, either from inetd or a listening TCP socket */
 	if (inetd_flag) {
 		server_accept_inetd(&sock_in, &sock_out);
 	} else {
+		platform_pre_listen();
 		server_listen();
 
 		if (options.protocol & SSH_PROTO_1)
 			generate_ephemeral_server_key();
 
 		signal(SIGHUP, sighup_handler);
 		signal(SIGCHLD, main_sigchld_handler);
 		signal(SIGTERM, sigterm_handler);
