From 00e1f2b8a86349acbaf14e654e4874525970cd01 Mon Sep 17 00:00:00 2001
From: Volker Klasen <volker.klasen@1und1.de>
Date: Fri, 14 Jan 2022 09:31:55 +0100
Subject: [PATCH] MDEV-26212: auth_pam: Replaced fork() + exec() by
 posix_spawn() to avoid ENOMEM when server process uses more memory than is
 still free and memory overcommit is disabled

---
 plugin/auth_pam/auth_pam.c | 71 ++++++++++++++++++--------------------
 1 file changed, 34 insertions(+), 37 deletions(-)

diff --git a/plugin/auth_pam/auth_pam.c b/plugin/auth_pam/auth_pam.c
index 35272c6b7cd..29d65d7bc07 100644
--- a/plugin/auth_pam/auth_pam.c
+++ b/plugin/auth_pam/auth_pam.c
@@ -23,6 +23,7 @@
 #include <mysql/plugin_auth.h>
 #include "auth_pam_tool.h"
 #include <my_global.h>
+#include <spawn.h>
 
 #ifndef DBUG_OFF
 static char pam_debug = 0;
@@ -43,6 +44,8 @@ static const int tool_name_len= 31;
 */
 static const unsigned int sleep_limit= 5;
 
+extern char **environ;
+
 static int pam_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
 {
   int p_to_c[2], c_to_p[2]; /* Parent-to-child and child-to-parent pipes. */
@@ -51,6 +54,7 @@ static int pam_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
   unsigned char field, *pkt;
   unsigned int n_sleep= 0;
   useconds_t sleep_time= 100;
+  posix_spawn_file_actions_t child_fd_actions;
 
   PAM_DEBUG((stderr, "PAM: opening pipes.\n"));
   if (pipe(p_to_c) < 0 || pipe(c_to_p) < 0)
@@ -58,54 +62,47 @@ static int pam_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
     /* Error creating pipes. */
     return CR_ERROR;
   }
-  PAM_DEBUG((stderr, "PAM: forking.\n"));
-  if ((proc_id= fork()) < 0)
+
+  PAM_DEBUG((stderr, "PAM: setting up file actions.\n"));
+  if (posix_spawn_file_actions_init(&child_fd_actions) ||
+      posix_spawn_file_actions_addclose(&child_fd_actions, p_to_c[1]) ||
+      posix_spawn_file_actions_addclose(&child_fd_actions, c_to_p[0]) ||
+      posix_spawn_file_actions_adddup2(&child_fd_actions, p_to_c[0], 0) ||
+      posix_spawn_file_actions_adddup2(&child_fd_actions, c_to_p[1], 1))
   {
-    /* Error forking. */
+    /* Error setting up file actions. */
     close(p_to_c[0]);
     close(c_to_p[1]);
     goto error_ret;
   }
 
-  if (proc_id == 0)
+  char toolpath[FN_REFLEN];
+  size_t plugin_dir_len= strlen(opt_plugin_dir);
+  PAM_DEBUG((stderr, "PAM: check tool directory: %s, %s.\n",
+                     opt_plugin_dir, tool_name));
+  if (plugin_dir_len + tool_name_len + 2 > sizeof(toolpath))
   {
-    /* The 'sandbox' process started. */
-    char toolpath[FN_REFLEN];
-    size_t plugin_dir_len= strlen(opt_plugin_dir);
-
-    PAM_DEBUG((stderr, "PAM: Child process prepares pipes.\n"));
-    
-    if (close(p_to_c[1]) < 0 ||
-        close(c_to_p[0]) < 0 ||
-        dup2(p_to_c[0], 0) < 0 || /* Parent's pipe to STDIN. */
-        dup2(c_to_p[1], 1) < 0)   /* Sandbox's pipe to STDOUT. */
-    {
-      exit(-1);
-    }
+    /* Tool path too long. */
+    close(p_to_c[0]);
+    close(c_to_p[1]);
+    goto error_ret;
+  }
 
-    PAM_DEBUG((stderr, "PAM: check tool directory: %s, %s.\n",
-                       opt_plugin_dir, tool_name));
-    if (plugin_dir_len + tool_name_len + 2 > sizeof(toolpath))
-    {
-      /* Tool path too long. */
-      exit(-1);
-    }
+  memcpy(toolpath, opt_plugin_dir, plugin_dir_len);
+  if (plugin_dir_len && toolpath[plugin_dir_len-1] != FN_LIBCHAR)
+    toolpath[plugin_dir_len++]= FN_LIBCHAR;
+  memcpy(toolpath+plugin_dir_len, tool_name, tool_name_len+1);
+  char *const argv[2]= {toolpath, NULL};
 
-    memcpy(toolpath, opt_plugin_dir, plugin_dir_len);
-    if (plugin_dir_len && toolpath[plugin_dir_len-1] != FN_LIBCHAR)
-      toolpath[plugin_dir_len++]= FN_LIBCHAR;
-    memcpy(toolpath+plugin_dir_len, tool_name, tool_name_len+1);
-
-    PAM_DEBUG((stderr, "PAM: execute pam sandbox [%s].\n", toolpath));
-    (void) execl(toolpath, toolpath, NULL);
-    PAM_DEBUG((stderr, "PAM: exec() failed.\n"));
-    my_printf_error(1, "PAM: Cannot execute %s (errno: %M)", ME_ERROR_LOG_ONLY,
-                    toolpath, errno);
-    exit(-1);
+  PAM_DEBUG((stderr, "PAM: spawn pam sandbox [%s].\n", toolpath));
+  if (posix_spawn(&proc_id, toolpath, &child_fd_actions, NULL, argv, environ))
+  {
+    /* Error spawning process. */
+    close(p_to_c[0]);
+    close(c_to_p[1]);
+    goto error_ret;
   }
 
-  /* Parent process continues. */
-
   PAM_DEBUG((stderr, "PAM: parent continues.\n"));
   if (close(p_to_c[0]) < 0 ||
       close(c_to_p[1]) < 0)
-- 
2.34.1

