Uploaded image for project: 'MariaDB Server'
  1. MariaDB Server
  2. MDEV-41233

auth_pam uses an uninitialized child PID in waitpid() and kill() after posix_spawn() fails

    XMLWordPrintable

Details

    • Bug
    • Status: Confirmed (View Workflow)
    • Major
    • Resolution: Unresolved
    • 10.11, 11.4, 11.8, 12.3, 13.1, 12.3.3
    • 10.11, 11.4, 11.8, 12.3, 13.1
    • None

    Description

      `auth_pam` declares `pid_t proc_id` without initialization. If `posix_spawn()`
      fails, control jumps to the common cleanup block, which repeatedly calls
      `waitpid(proc_id, ...)` and can later call `kill(proc_id, SIGKILL)`.

      The product-level reproducer makes only the `auth_pam_tool` spawn return
      `EAGAIN`, writes a fixed sentinel into the output PID, and safely suppresses
      `waitpid`/`kill` for that sentinel. A real PAM login then causes repeated
      `waitpid(0x414141, ...)` calls and `kill(0x414141, SIGKILL)`. All calls for
      other PIDs pass through unchanged.

      Steps to reproduce

      Run only on a disposable host. The shim intentionally prevents the invalid
      signal from reaching the kernel.

      #!/usr/bin/env bash
      set -euo pipefail
      IMAGE='mariadb@sha256:dd9b303aed4f4890ed09f766d8ca9ddfd176c0c6f6267feff53b3192ec65a979'
      C="mdev-mcf2s08-$$"; OWNER="mdev-mcf2s08-$$"; TMP=$(mktemp -d)
      cleanup() {
        if docker inspect --format '{{ index .Config.Labels "io.encryptiondbfuzz.owner" }}' \
             "$C" 2>/dev/null | grep -Fqx "$OWNER"; then
          docker rm -f "$C" >/dev/null 2>&1 || true
        fi
        rm -rf -- "$TMP"
      }
      trap cleanup EXIT
       
      cat >"$TMP/fail.c" <<'EOF_C'
      #define _GNU_SOURCE
      #include <dlfcn.h>
      #include <errno.h>
      #include <signal.h>
      #include <spawn.h>
      #include <stdio.h>
      #include <string.h>
      #include <sys/types.h>
      #include <sys/wait.h>
      #include <unistd.h>
      static const pid_t marker=(pid_t)0x414141;
      static void note(const char *op,pid_t pid,int value){
        FILE *f=fopen("/tmp/pam-spawn.log","a");
        if(f){fprintf(f,"%s pid=%ld value=%d\n",op,(long)pid,value);fclose(f);}
      }
      int posix_spawn(pid_t *pid,const char *path,const posix_spawn_file_actions_t *a,
                      const posix_spawnattr_t *attr,char *const argv[],char *const envp[]){
        static int (*real_fn)(pid_t*,const char*,const posix_spawn_file_actions_t*,
                              const posix_spawnattr_t*,char *const[],char *const[]);
        if(strstr(path,"auth_pam_tool")){if(pid)*pid=marker;note("posix_spawn_fail",marker,EAGAIN);return EAGAIN;}
        if(!real_fn)real_fn=dlsym(RTLD_NEXT,"posix_spawn");
        return real_fn(pid,path,a,attr,argv,envp);
      }
      pid_t waitpid(pid_t pid,int *status,int options){
        static pid_t (*real_fn)(pid_t,int*,int);
        if(pid==marker){(void)status;(void)options;note("waitpid_after_spawn_failure",pid,ECHILD);errno=ECHILD;return -1;}
        if(!real_fn)real_fn=dlsym(RTLD_NEXT,"waitpid");
        return real_fn(pid,status,options);
      }
      int kill(pid_t pid,int sig){
        static int (*real_fn)(pid_t,int);
        if(pid==marker){note("kill_after_spawn_failure",pid,sig);errno=ESRCH;return -1;}
        if(!real_fn)real_fn=dlsym(RTLD_NEXT,"kill");
        return real_fn(pid,sig);
      }
      EOF_C
      gcc -shared -fPIC -O2 -Wall -Wextra -Werror -o "$TMP/fail.so" "$TMP/fail.c" -ldl
      chmod 755 "$TMP"; chmod 444 "$TMP/fail.so"
       
      docker run -d --name "$C" --label "io.encryptiondbfuzz.owner=$OWNER" \
        --network none --read-only --cap-drop ALL \
        --security-opt no-new-privileges=true --pids-limit 160 --memory 1100m \
        --cpus 1 --user 999:999 \
        --tmpfs /var/lib/mysql:rw,nosuid,nodev,size=512m,uid=999,gid=999 \
        --tmpfs /run/mysqld:rw,nosuid,nodev,size=16m,uid=999,gid=999 \
        --tmpfs /tmp:rw,nosuid,nodev,size=64m,uid=999,gid=999 \
        --mount "type=bind,src=$TMP,dst=/edbf,readonly" \
        --env LD_PRELOAD=/edbf/fail.so --env MARIADB_ALLOW_EMPTY_ROOT_PASSWORD=1 \
        "$IMAGE" >/dev/null
       
      READY=0
      for I in $(seq 1 90); do
        if docker exec --user 999:999 --env LD_PRELOAD= "$C" mariadb --protocol=socket \
             --socket=/run/mysqld/mysqld.sock -uroot -NBe 'SELECT VERSION()' 2>/dev/null; then READY=1; break; fi
        sleep 1
      done
      test "$READY" -eq 1
      SQL=(docker exec --user 999:999 --env LD_PRELOAD= "$C" mariadb --protocol=socket --socket=/run/mysqld/mysqld.sock -uroot --batch --skip-column-names)
      "${SQL[@]}" -e "INSTALL SONAME 'auth_pam'; CREATE USER pam_probe@localhost IDENTIFIED VIA pam USING 'mariadb'"
      set +e
      docker exec --user 999:999 --env LD_PRELOAD= "$C" timeout 10 mariadb \
        --protocol=socket --socket=/run/mysqld/mysqld.sock -upam_probe -pwrong \
        --batch --skip-column-names -e 'SELECT 1'
      RC=$?
      set -e
      echo "login_rc=$RC"
      docker exec --user 999:999 "$C" cat /tmp/pam-spawn.log
      "${SQL[@]}" -e "SELECT CONCAT('server_health=',1)"
      

      Actual result

      ERROR 1045 (28000): Access denied for user 'pam_probe'@'localhost' (...)
      login_rc=1
      posix_spawn_fail pid=4276545 value=11
      waitpid_after_spawn_failure pid=4276545 value=10
      ... repeated waitpid calls ...
      kill_after_spawn_failure pid=4276545 value=9
      waitpid_after_spawn_failure pid=4276545 value=10
      server_health=1
      

      The fixed sentinel is decimal 4276545 (`0x414141`). The shim suppressed only
      cleanup operations for that value, so the test did not signal any real process.
      The server remained healthy and did not crash.

      Expected result

      If `posix_spawn()` fails, the PAM plugin should close its local resources and
      return the authentication error without waiting for or signaling a child that
      was never created. `proc_id` must only be used after a successful spawn.

      Attachments

        Issue Links

          Activity

            People

              holyfoot Alexey Botchkov
              csfuzz csfuzz
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

                Created:
                Updated:

                Git Integration

                  Error rendering 'com.xiplink.jira.git.jira_git_plugin:git-issue-webpanel'. Please contact your Jira administrators.