Version in base suite: 24.11.5-4 Base version: slurm-wlm_24.11.5-4 Target version: slurm-wlm_24.11.5-4+deb13u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/s/slurm-wlm/slurm-wlm_24.11.5-4.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/s/slurm-wlm/slurm-wlm_24.11.5-4+deb13u1.dsc changelog | 27 ++ patches/CVE-2026-65107-1.patch | 84 +++++++ patches/CVE-2026-65107-2.patch | 49 ++++ patches/CVE-2026-65107-3.patch | 85 ++++++++ patches/CVE-2026-65108.patch | 33 +++ patches/CVE-2026-65109-1.patch | 168 +++++++++++++++ patches/CVE-2026-65109-2.patch | 180 +++++++++++++++++ patches/CVE-2026-65138-1.patch | 26 ++ patches/CVE-2026-65138-2.patch | 26 ++ patches/CVE-2026-65139-1.patch | 422 ++++++++++++++++++++++++++++++++++++++++ patches/CVE-2026-65139-2.patch | 156 ++++++++++++++ patches/CVE-2026-65139-3.patch | 34 +++ patches/CVE-2026-65139-4.patch | 432 +++++++++++++++++++++++++++++++++++++++++ patches/CVE-2026-65139-5.patch | 57 +++++ patches/CVE-2026-65140-1.patch | 153 ++++++++++++++ patches/CVE-2026-65140-2.patch | 74 +++++++ patches/CVE-2026-65140-3.patch | 55 +++++ patches/CVE-2026-65140-4.patch | 32 +++ patches/CVE-2026-65140-5.patch | 109 ++++++++++ patches/CVE-2026-65140-6.patch | 64 ++++++ patches/CVE-2026-65165-1.patch | 90 ++++++++ patches/CVE-2026-65165-2.patch | 38 +++ patches/CVE-2026-65165-3.patch | 36 +++ patches/CVE-2026-65168-1.patch | 63 +++++ patches/CVE-2026-65168-2.patch | 38 +++ patches/CVE-2026-65168-3.patch | 37 +++ patches/CVE-2026-65168-4.patch | 28 ++ patches/series | 26 ++ 28 files changed, 2622 insertions(+) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpg1d966rb/slurm-wlm_24.11.5-4.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpg1d966rb/slurm-wlm_24.11.5-4+deb13u1.dsc: no acceptable signature found diff -Nru slurm-wlm-24.11.5/debian/changelog slurm-wlm-24.11.5/debian/changelog --- slurm-wlm-24.11.5/debian/changelog 2025-07-13 17:39:23.000000000 +0000 +++ slurm-wlm-24.11.5/debian/changelog 2026-09-07 07:28:59.000000000 +0000 @@ -1,3 +1,30 @@ +slurm-wlm (24.11.5-4+deb13u1) trixie-security; urgency=high + + * Non-maintainer upload. + * Fix multiple security issues by backporting the upstream fixes from the + slurm-25.05 branch (Closes: #1146563): + * CVE-2026-65107: sbcast shared objects skipped credential verification, + and slurmd could crash on invalid sbcast filenames. + * CVE-2026-65108: slurmstepd stack overflow when a job environment + contains an oversized SPANK option variable. + * CVE-2026-65109: slurmstepd could remove files outside the container + spool directory when cleaning up an OCI container, and left spool + directories behind when ContainerPath contains a task id pattern. + * CVE-2026-65138: heap over-read and NULL pointer dereference in slurmd + when unpacking a malformed forward data RPC. + * CVE-2026-65139: slurmdbd used unsafe cluster names and non-numeric id + lists in accounting database queries. + * CVE-2026-65140: privilege escalation where an operator could alter + Administrator accounts through the accounting database. + * CVE-2026-65165: a job step using arbitrary distribution could end up + with a node count disagreeing with its node list. The count is now taken + from the list, hostlist functions are rejected in it, and a list that + disagrees with the requested node count is refused. + * Fix possible slurmstepd crash on step socket requests with invalid + lengths. + + -- Aron Xu Mon, 07 Sep 2026 15:28:59 +0800 + slurm-wlm (24.11.5-4) unstable; urgency=medium * Move adduser to postinst diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65107-1.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65107-1.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65107-1.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65107-1.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,84 @@ +From 74f2069434cc58fd544966598f0f65a5b6b6a21e Mon Sep 17 00:00:00 2001 +From: Tim McMullan +Date: Fri, 15 May 2026 11:18:05 -0400 +Subject: [PATCH] Verify sbcast credential signatures for FILE_BCAST_SO + transfers + +--- + src/interfaces/cred.c | 2 +- + src/plugins/cred/munge/cred_munge.c | 19 +++++++++++++------ + 2 files changed, 14 insertions(+), 7 deletions(-) + +diff --git a/src/interfaces/cred.c b/src/interfaces/cred.c +index 7faf6a2d9b..4dcfcf6297 100644 +--- a/src/interfaces/cred.c ++++ b/src/interfaces/cred.c +@@ -740,7 +740,7 @@ extern sbcast_cred_t *unpack_sbcast_cred(buf_t *buffer, void *msg, + file_bcast_msg_t *bmsg = msg; + bool verify = false; + +- if (bmsg && (bmsg->block_no == 1) && !(bmsg->flags & FILE_BCAST_SO)) ++ if (bmsg && (bmsg->block_no == 1)) + verify = true; + + return (*(ops.sbcast_unpack))(buffer, verify, protocol_version); +diff --git a/src/plugins/cred/munge/cred_munge.c b/src/plugins/cred/munge/cred_munge.c +index 8579c097b7..20903c1bd9 100644 +--- a/src/plugins/cred/munge/cred_munge.c ++++ b/src/plugins/cred/munge/cred_munge.c +@@ -233,7 +233,14 @@ again: + if (err == EMUNGE_SOCKET) + error("If munged is up, restart with --num-threads=10"); + +- if (err != EMUNGE_CRED_REPLAYED) { ++ /* ++ * An sbcast credential is presented once per file, so munged ++ * sees it repeatedly and eventually past its own ttl. The ++ * signed expiration carried in the credential is checked by ++ * the caller and is the authoritative lifetime. ++ */ ++ if ((err != EMUNGE_CRED_REPLAYED) && ++ !(replay_okay && (err == EMUNGE_CRED_EXPIRED))) { + rc = err; + goto end_it; + } +@@ -243,7 +250,7 @@ again: + goto end_it; + } + +- debug2("We had a replayed credential, but this is expected."); ++ debug2("Credential was replayed or expired, but this is expected."); + } + + if ((uid != slurm_conf.slurm_user_id) && (uid != 0)) { +@@ -272,10 +279,10 @@ end_it: + return rc; + } + +-static int _verify_signature(char *buffer, uint32_t buf_size, char *signature) ++static int _verify_signature(char *buffer, uint32_t buf_size, char *signature, ++ bool replay_okay) + { + int rc = SLURM_SUCCESS; +- bool replay_okay = false; + buf_t *payload = NULL; + + #ifdef MULTIPLE_SLURMD +@@ -331,7 +338,7 @@ extern slurm_cred_t *cred_p_unpack(buf_t *buf, uint16_t protocol_version) + if (credential->signature && running_in_slurmd()) { + if (_verify_signature(get_buf_data(credential->buffer), + credential->sig_offset, +- credential->signature)) { ++ credential->signature, false)) { + slurm_cred_destroy(credential); + return NULL; + } +@@ -424,7 +431,7 @@ extern sbcast_cred_t *sbcast_p_unpack(buf_t *buf, bool verify, + } + + if (_verify_signature(get_buf_data(buf) + cred_start, +- siglen, cred->signature)) { ++ siglen, cred->signature, true)) { + delete_sbcast_cred(cred); + return NULL; + } diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65107-2.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65107-2.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65107-2.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65107-2.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,49 @@ +From e905703c341dcda106ef9927f15e411ec71450b5 Mon Sep 17 00:00:00 2001 +From: Tim McMullan +Date: Fri, 15 May 2026 11:18:05 -0400 +Subject: [PATCH] slurmd - Reject invalid sbcast filenames + +Reviewed-by: Aron Xu + +--- + src/slurmd/slurmd/req.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/src/slurmd/slurmd/req.c b/src/slurmd/slurmd/req.c +index 7f5fda5ef4..b9a54a31a9 100644 +--- a/src/slurmd/slurmd/req.c ++++ b/src/slurmd/slurmd/req.c +@@ -4475,6 +4475,22 @@ static void _rpc_file_bcast(slurm_msg_t *msg) + }; + char *fname = NULL; + ++ if (!req->fname || !req->fname[0] || ++ xstrchr(req->fname, '/') || ++ !xstrcmp(req->fname, "..")) { ++ error("Invalid sbcast shared object filename `%s` from uid %u", ++ req->fname, key.uid); ++ rc = SLURM_ERROR; ++ goto done; ++ } ++ ++ if (!req->exe_fname || !req->exe_fname[0]) { ++ error("Missing sbcast executable path from uid %u", ++ key.uid); ++ rc = SLURM_ERROR; ++ goto done; ++ } ++ + slurm_rwlock_rdlock(&file_bcast_lock); + if (!(libdir = list_find_first(bcast_libdir_list, + _find_libdir_record, +@@ -4491,6 +4507,10 @@ static void _rpc_file_bcast(slurm_msg_t *msg) + xfree(req->fname); + req->fname = fname; + slurm_rwlock_unlock(&file_bcast_lock); ++ } else if (!req->fname || !req->fname[0]) { ++ error("Invalid sbcast filename from uid %u", key.uid); ++ rc = SLURM_ERROR; ++ goto done; + } else if (req->fname[strlen(req->fname) - 1] == '/') { + /* + * "srun --bcast" was called with a target directory instead of diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65107-3.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65107-3.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65107-3.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65107-3.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,85 @@ +From 2f0293725c153e2042d5694314d3cad3f2e69fa3 Mon Sep 17 00:00:00 2001 +From: Tim McMullan +Date: Mon, 17 Aug 2026 11:12:00 -0400 +Subject: [PATCH] cred - Only allow sbcast credential replay for shared objects + +The replay tolerance that shared object transfers need was applied to +every sbcast credential. The executable is always the first decode of a +given credential and is never replayed, so pass the flag down from +unpack_sbcast_cred() and leave that path strict. + +--- + src/interfaces/cred.c | 15 ++++++++++++--- + src/plugins/cred/munge/cred_munge.c | 3 ++- + src/plugins/cred/none/cred_none.c | 1 + + 3 files changed, 15 insertions(+), 4 deletions(-) + +diff --git a/src/interfaces/cred.c b/src/interfaces/cred.c +index 4dcfcf6297..d0ef8dcb75 100644 +--- a/src/interfaces/cred.c ++++ b/src/interfaces/cred.c +@@ -80,6 +80,7 @@ typedef struct { + sbcast_cred_t *(*sbcast_create) (sbcast_cred_arg_t *cred, + uint16_t protocol_version); + sbcast_cred_t *(*sbcast_unpack) (buf_t *buffer, bool verify, ++ bool replay_okay, + uint16_t protocol_version); + } slurm_cred_ops_t; + +@@ -738,12 +739,20 @@ extern sbcast_cred_t *unpack_sbcast_cred(buf_t *buffer, void *msg, + uint16_t protocol_version) + { + file_bcast_msg_t *bmsg = msg; +- bool verify = false; ++ bool verify = false, replay_okay = false; + +- if (bmsg && (bmsg->block_no == 1)) ++ if (bmsg && (bmsg->block_no == 1)) { + verify = true; ++ /* ++ * One credential covers the executable and every shared ++ * object sent after it, so munged only sees it a second ++ * time on the shared object transfers. ++ */ ++ replay_okay = (bmsg->flags & FILE_BCAST_SO); ++ } + +- return (*(ops.sbcast_unpack))(buffer, verify, protocol_version); ++ return (*(ops.sbcast_unpack))(buffer, verify, replay_okay, ++ protocol_version); + } + + extern void print_sbcast_cred(sbcast_cred_t *sbcast_cred) +diff --git a/src/plugins/cred/munge/cred_munge.c b/src/plugins/cred/munge/cred_munge.c +index 20903c1bd9..6ef8f6b5be 100644 +--- a/src/plugins/cred/munge/cred_munge.c ++++ b/src/plugins/cred/munge/cred_munge.c +@@ -413,6 +413,7 @@ extern sbcast_cred_t *sbcast_p_create(sbcast_cred_arg_t *cred_arg, + } + + extern sbcast_cred_t *sbcast_p_unpack(buf_t *buf, bool verify, ++ bool replay_okay, + uint16_t protocol_version) + { + sbcast_cred_t *cred; +@@ -431,7 +432,7 @@ extern sbcast_cred_t *sbcast_p_unpack(buf_t *buf, bool verify, + } + + if (_verify_signature(get_buf_data(buf) + cred_start, +- siglen, cred->signature, true)) { ++ siglen, cred->signature, replay_okay)) { + delete_sbcast_cred(cred); + return NULL; + } +diff --git a/src/plugins/cred/none/cred_none.c b/src/plugins/cred/none/cred_none.c +index b85b9c88fb..33914929d0 100644 +--- a/src/plugins/cred/none/cred_none.c ++++ b/src/plugins/cred/none/cred_none.c +@@ -139,6 +139,7 @@ extern sbcast_cred_t *sbcast_p_create(sbcast_cred_arg_t *cred_arg, + } + + extern sbcast_cred_t *sbcast_p_unpack(buf_t *buf, bool verify, ++ bool replay_okay, + uint16_t protocol_version) + { + uint32_t siglen; diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65108.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65108.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65108.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65108.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,33 @@ +From 07e814c3a62c644cc913aadbc1d276284e3e103b Mon Sep 17 00:00:00 2001 +From: Tim McMullan +Date: Tue, 4 Aug 2026 00:00:00 -0600 +Subject: [PATCH] Fix stack overflow clearing spank option environment + +spank_clear_remote_options_env() copied the variable name into a fixed +1024 byte stack buffer with an unbounded memcpy(), so an oversized SPANK +option variable in the job environment smashed the stack while running as +root. Use xstrndup() instead. + +--- + src/common/spank.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/common/spank.c b/src/common/spank.c +index 6ec692f738..c9262b5e49 100644 +--- a/src/common/spank.c ++++ b/src/common/spank.c +@@ -1743,11 +1743,11 @@ void spank_clear_remote_options_env (char **env) + if (xstrncmp (p, SPANK_OPTION_ENV_PREFIX, len) == 0) { + char *end = strchr (p+len, '='); + if (end) { +- char name[1024]; +- memcpy (name, *ep, end - *ep); +- name [end - *ep] = '\0'; ++ char *name = xstrndup(*ep, end - *ep); ++ + debug("unsetenv (%s)", name); + unsetenvp (env, name); ++ xfree(name); + } + } + } diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65109-1.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65109-1.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65109-1.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65109-1.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,168 @@ +From 9f5543cfec99285a27005cbbc3fb9d0013c486dd Mon Sep 17 00:00:00 2001 +From: Tim McMullan +Date: Mon, 10 Aug 2026 15:28:14 -0400 +Subject: [PATCH] runtime/oci - Do not resolve container spool paths as root on + cleanup + +_cleanup_container() removed the spool contents with path based unlink() +and rmdir() as root, but the job user owns the directory components, so +swapping a task directory for a symlink made root delete the target's +config.json and environment. Descend from the configured spool directory +with O_NOFOLLOW and remove relative to those handles. + +Reviewed-by: Aron Xu + +--- + src/slurmd/slurmstepd/container.c | 106 +++++++++++++++++++++++------- + 1 file changed, 83 insertions(+), 23 deletions(-) + +diff --git a/src/slurmd/slurmstepd/container.c b/src/slurmd/slurmstepd/container.c +index 81e68d2dfc..c58408a7c1 100644 +--- a/src/slurmd/slurmstepd/container.c ++++ b/src/slurmd/slurmstepd/container.c +@@ -35,6 +35,7 @@ + + #include "config.h" + ++#include + #include + #include + #include +@@ -1184,6 +1185,8 @@ extern void container_run(stepd_step_rec_t *step, + extern void cleanup_container(stepd_step_rec_t *step) + { + step_container_t *c = step->container; ++ char *parent = NULL, *stepdir = NULL, *stepname = NULL; ++ int basefd = -1, stepfd = -1; + + xassert(c->magic == STEP_CONTAINER_MAGIC); + +@@ -1201,48 +1204,105 @@ extern void cleanup_container(stepd_step_rec_t *step) + if (oci_conf->disable_cleanup) + goto done; + ++ /* swap to non-task spool_dir */ ++ xfree(c->spool_dir); ++ c->spool_dir = _generate_spooldir(step, NULL); ++ ++ if (!c->spool_dir) { ++ error("%s: unable to generate container spool directory", ++ __func__); ++ goto cleanup_fds; ++ } ++ ++ /* ++ * The job user owns the step spool directory and can replace any ++ * component below it with a symlink, so never resolve these paths as ++ * root. Descend with O_NOFOLLOW and remove relative to the directory ++ * handles instead. ++ * ++ * The spool dir patterns end with a separator, which would leave an ++ * empty basename, so drop any trailing separators first. ++ */ ++ stepdir = xstrdup(c->spool_dir); ++ for (int i = strlen(stepdir) - 1; (i > 0) && (stepdir[i] == '/'); i--) ++ stepdir[i] = '\0'; ++ ++ parent = xdirname(stepdir); ++ stepname = xbasename(stepdir); ++ ++ if ((basefd = open(parent, O_DIRECTORY)) < 0) { ++ if (errno != ENOENT) ++ error("open(%s): %m", parent); ++ goto cleanup_fds; ++ } ++ ++ if ((stepfd = openat(basefd, stepname, ++ (O_DIRECTORY | O_NOFOLLOW))) < 0) { ++ if (errno != ENOENT) ++ error("openat(%s/%s): %m", parent, stepname); ++ goto cleanup_fds; ++ } ++ + if (step->node_tasks > 0) { + /* clear every config.json and task dir */ + for (int i = 0; i < step->node_tasks; i++) { ++ int taskfd = -1; ++ char *taskpath = NULL, *taskdir = NULL; ++ + xfree(c->spool_dir); + c->spool_dir = _generate_spooldir(step, step->task[i]); ++ + _generate_patterns(step, step->task[i]); + +- if (!oci_conf->ignore_config_json) { +- char *jconfig = NULL; ++ taskpath = xstrdup(c->spool_dir); ++ for (int j = strlen(taskpath) - 1; ++ (j > 0) && (taskpath[j] == '/'); j--) ++ taskpath[j] = '\0'; + +- xstrfmtcat(jconfig, "%s/config.json", +- c->spool_dir); ++ taskdir = xbasename(taskpath); + +- if ((unlink(jconfig) < 0) && (errno != ENOENT)) +- error("unlink(%s): %m", jconfig); +- xfree(jconfig); ++ if ((taskfd = openat(stepfd, taskdir, ++ (O_DIRECTORY | O_NOFOLLOW))) < 0) { ++ if (errno != ENOENT) ++ error("openat(%s): %m", c->spool_dir); ++ xfree(taskpath); ++ continue; + } + +- if (oci_conf->create_env_file) { +- char *envfile = NULL; ++ if (!oci_conf->ignore_config_json && ++ unlinkat(taskfd, "config.json", 0) && ++ (errno != ENOENT)) ++ error("unlinkat(%s/config.json): %m", ++ c->spool_dir); + +- xstrfmtcat(envfile, "%s/%s", c->spool_dir, +- SLURM_CONTAINER_ENV_FILE); ++ if (oci_conf->create_env_file && ++ unlinkat(taskfd, SLURM_CONTAINER_ENV_FILE, 0) && ++ (errno != ENOENT)) ++ error("unlinkat(%s/%s): %m", c->spool_dir, ++ SLURM_CONTAINER_ENV_FILE); + +- if (unlink(envfile) && (errno != ENOENT)) +- error("unlink(%s): %m", envfile); ++ fd_close(&taskfd); + +- xfree(envfile); +- } ++ if (unlinkat(stepfd, taskdir, AT_REMOVEDIR) && ++ (errno != ENOENT)) ++ error("unlinkat(%s): %m", c->spool_dir); + +- if (rmdir(c->spool_dir) && (errno != ENOENT)) +- error("rmdir(%s): %m", c->spool_dir); +- xfree(c->spool_dir); ++ xfree(taskpath); + } ++ ++ /* swap back to non-task spool_dir */ ++ xfree(c->spool_dir); ++ c->spool_dir = _generate_spooldir(step, NULL); + } + +- /* swap to non-task spool_dir */ +- xfree(c->spool_dir); +- c->spool_dir = _generate_spooldir(step, NULL); ++ if (unlinkat(basefd, stepname, AT_REMOVEDIR) && (errno != ENOENT)) ++ error("unlinkat(%s): %m", c->spool_dir); + +- if (rmdir(c->spool_dir) && (errno != ENOENT)) +- error("rmdir(%s): %m", c->spool_dir); ++cleanup_fds: ++ fd_close(&stepfd); ++ fd_close(&basefd); ++ xfree(parent); ++ xfree(stepdir); + + done: + FREE_NULL_OCI_CONF(oci_conf); diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65109-2.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65109-2.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65109-2.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65109-2.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,180 @@ +From 7e1b5e852471baad0ba74414f7865a917248a836 Mon Sep 17 00:00:00 2001 +From: Tim McMullan +Date: Tue, 11 Aug 2026 16:58:19 -0400 +Subject: [PATCH] runtime/oci - Handle a taskid pattern in ContainerPath on + cleanup + +_generate_spooldir() trims the pattern at the first taskid component, so +with a %t in ContainerPath the step spool dir ends in a '/' and its +basename is empty, and the task dirs sit several components below it +rather than directly beneath. Strip the trailing separators and walk the +intervening components with O_NOFOLLOW. + +Reviewed-by: Aron Xu + +--- + src/slurmd/slurmstepd/container.c | 119 +++++++++++++++++++++++++++--- + 1 file changed, 107 insertions(+), 12 deletions(-) + +diff --git a/src/slurmd/slurmstepd/container.c b/src/slurmd/slurmstepd/container.c +index c58408a7c1..2e5d9ab5d7 100644 +--- a/src/slurmd/slurmstepd/container.c ++++ b/src/slurmd/slurmstepd/container.c +@@ -1182,6 +1182,64 @@ extern void container_run(stepd_step_rec_t *step, + _create_start(step, task); + } + ++/* ++ * Return the portion of path below base, or NULL if path is not below base. ++ * Caller must xfree() the result. ++ */ ++static char *_relative_subpath(const char *base, const char *path) ++{ ++ size_t len = strlen(base); ++ ++ if (xstrncmp(path, base, len)) ++ return NULL; ++ ++ path += len; ++ while (path[0] == '/') ++ path++; ++ ++ if (!path[0]) ++ return NULL; ++ ++ return xstrdup(path); ++} ++ ++/* ++ * Open the directory holding stop, descending rel from basefd one component ++ * at a time with O_NOFOLLOW so no component can be swapped for a symlink. ++ * IN basefd - directory to descend from ++ * IN rel - path relative to basefd ++ * IN stop - pointer into rel to the component to stop before ++ * RET fd of the parent directory, or -1 with errno set ++ */ ++static int _open_subdir(int basefd, char *rel, const char *stop) ++{ ++ int fd; ++ ++ if ((fd = dup(basefd)) < 0) ++ return -1; ++ ++ for (char *tok = rel; tok < stop;) { ++ int next; ++ char *sep = xstrchr(tok, '/'); ++ ++ if (!sep) ++ break; ++ ++ *sep = '\0'; ++ next = openat(fd, tok, (O_DIRECTORY | O_NOFOLLOW)); ++ *sep = '/'; ++ fd_close(&fd); ++ ++ if (next < 0) ++ return -1; ++ ++ fd = next; ++ tok = sep + 1; ++ } ++ ++ return fd; ++} ++ + extern void cleanup_container(stepd_step_rec_t *step) + { + step_container_t *c = step->container; +@@ -1220,8 +1278,10 @@ extern void cleanup_container(stepd_step_rec_t *step) + * root. Descend with O_NOFOLLOW and remove relative to the directory + * handles instead. + * +- * The spool dir patterns end with a separator, which would leave an +- * empty basename, so drop any trailing separators first. ++ * ContainerPath may hold a taskid pattern, in which case the step ++ * spool dir is the pattern trimmed at that component and so ends in a ++ * '/'. The default patterns end in a '/' too. Drop any trailing ++ * separators before splitting it. + */ + stepdir = xstrdup(c->spool_dir); + for (int i = strlen(stepdir) - 1; (i > 0) && (stepdir[i] == '/'); i--) +@@ -1246,8 +1306,8 @@ extern void cleanup_container(stepd_step_rec_t *step) + if (step->node_tasks > 0) { + /* clear every config.json and task dir */ + for (int i = 0; i < step->node_tasks; i++) { +- int taskfd = -1; +- char *taskpath = NULL, *taskdir = NULL; ++ int taskfd = -1, taskparentfd = -1; ++ char *taskpath = NULL, *rel = NULL, *taskdir = NULL; + + xfree(c->spool_dir); + c->spool_dir = _generate_spooldir(step, step->task[i]); +@@ -1259,14 +1319,44 @@ extern void cleanup_container(stepd_step_rec_t *step) + (j > 0) && (taskpath[j] == '/'); j--) + taskpath[j] = '\0'; + +- taskdir = xbasename(taskpath); ++ /* ++ * The task dir is not always a direct child of the ++ * step dir: a taskid pattern in ContainerPath leaves ++ * intervening components. Walk them one at a time. ++ * ContainerPath without any taskid pattern leaves the ++ * task dir equal to the step dir, which is only ++ * removed after the loop. ++ */ ++ rel = _relative_subpath(stepdir, taskpath); ++ ++ if (rel) { ++ taskdir = xbasename(rel); ++ taskparentfd = _open_subdir(stepfd, rel, ++ taskdir); ++ ++ if (taskparentfd < 0) { ++ if (errno != ENOENT) ++ error("openat(%s): %m", ++ c->spool_dir); ++ goto next_task; ++ } + +- if ((taskfd = openat(stepfd, taskdir, +- (O_DIRECTORY | O_NOFOLLOW))) < 0) { +- if (errno != ENOENT) +- error("openat(%s): %m", c->spool_dir); +- xfree(taskpath); +- continue; ++ if ((taskfd = openat(taskparentfd, taskdir, ++ (O_DIRECTORY | ++ O_NOFOLLOW))) < 0) { ++ if (errno != ENOENT) ++ error("openat(%s): %m", ++ c->spool_dir); ++ goto next_task; ++ } ++ } else if (xstrcmp(taskpath, stepdir)) { ++ error("%s: %s is not below %s", ++ __func__, c->spool_dir, stepdir); ++ goto next_task; ++ } else if ((taskfd = dup(stepfd)) < 0) { ++ error("%s: dup(%s): %m", __func__, ++ c->spool_dir); ++ goto next_task; + } + + if (!oci_conf->ignore_config_json && +@@ -1283,10 +1373,15 @@ extern void cleanup_container(stepd_step_rec_t *step) + + fd_close(&taskfd); + +- if (unlinkat(stepfd, taskdir, AT_REMOVEDIR) && ++ if ((taskparentfd >= 0) && ++ unlinkat(taskparentfd, taskdir, AT_REMOVEDIR) && + (errno != ENOENT)) + error("unlinkat(%s): %m", c->spool_dir); + ++next_task: ++ fd_close(&taskfd); ++ fd_close(&taskparentfd); ++ xfree(rel); + xfree(taskpath); + } + diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65138-1.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65138-1.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65138-1.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65138-1.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,26 @@ +From ba4728734b2fd03b6a95903b8edfa5a7c04335be Mon Sep 17 00:00:00 2001 +From: Tim McMullan +Date: Thu, 9 Jul 2026 09:25:38 -0400 +Subject: [PATCH] Fix heap over-read in REQUEST_FORWARD_DATA unpack + +msg->len was unpacked independently of the data buffer's actual length, +letting a crafted RPC over-read in _rpc_forward_data()'s safe_write(). +Reject mismatched lengths. + +--- + src/common/slurm_protocol_pack.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/common/slurm_protocol_pack.c b/src/common/slurm_protocol_pack.c +index ce25ab76c6..4681eeae7e 100644 +--- a/src/common/slurm_protocol_pack.c ++++ b/src/common/slurm_protocol_pack.c +@@ -12027,6 +12027,8 @@ static int _unpack_forward_data_msg(forward_data_msg_t **msg_ptr, + safe_unpackstr(&msg->address, buffer); + safe_unpack32(&msg->len, buffer); + safe_unpackmem_xmalloc(&msg->data, &temp32, buffer); ++ if (msg->len != temp32) ++ goto unpack_error; + + return SLURM_SUCCESS; + diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65138-2.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65138-2.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65138-2.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65138-2.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,26 @@ +From 964841bc4872ad80323778fb050d625a48def5b8 Mon Sep 17 00:00:00 2001 +From: Tim McMullan +Date: Tue, 4 Aug 2026 14:20:03 -0400 +Subject: [PATCH] Fix NULL dereference in REQUEST_FORWARD_DATA unpack + +_unpack_forward_data_msg() accepted a missing address, which unpacks to +NULL and reaches strlen() in _connect_as_other(), crashing slurmd. Reject +it at unpack instead. + +--- + src/common/slurm_protocol_pack.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/common/slurm_protocol_pack.c b/src/common/slurm_protocol_pack.c +index 4681eeae7e..2845b32abe 100644 +--- a/src/common/slurm_protocol_pack.c ++++ b/src/common/slurm_protocol_pack.c +@@ -12025,6 +12025,8 @@ static int _unpack_forward_data_msg(forward_data_msg_t **msg_ptr, + msg = xmalloc(sizeof(forward_data_msg_t)); + *msg_ptr = msg; + safe_unpackstr(&msg->address, buffer); ++ if (!msg->address) ++ goto unpack_error; + safe_unpack32(&msg->len, buffer); + safe_unpackmem_xmalloc(&msg->data, &temp32, buffer); + if (msg->len != temp32) diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65139-1.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65139-1.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65139-1.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65139-1.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,422 @@ +From 39f1099524bf9b0dd3ceee2c62b230d73c16a4ae Mon Sep 17 00:00:00 2001 +From: Tim McMullan +Date: Thu, 9 Jul 2026 10:43:40 -0400 +Subject: [PATCH] accounting_storage/mysql - Reject unsafe cluster names + +Cluster names are interpolated into table identifiers, not just quoted +values, and identifier quoting can't be backslash-escaped, so escaping +can't neutralize them. Reject names with SQL-unsafe characters instead. + +Reviewed-by: Aron Xu + +--- + .../mysql/accounting_storage_mysql.c | 25 +++++++++++++++++++ + .../mysql/accounting_storage_mysql.h | 7 ++++++ + .../mysql/as_mysql_archive.c | 7 ++++++ + .../accounting_storage/mysql/as_mysql_assoc.c | 19 ++++++++++++++ + .../mysql/as_mysql_cluster.c | 20 +++++++++++++++ + .../mysql/as_mysql_federation.c | 10 ++++++++ + .../mysql/as_mysql_jobacct_process.c | 5 ++++ + .../mysql/as_mysql_problems.c | 15 +++++++++++ + .../mysql/as_mysql_resource.c | 13 ++++++++++ + .../accounting_storage/mysql/as_mysql_resv.c | 4 +++ + .../accounting_storage/mysql/as_mysql_txn.c | 4 +++ + .../accounting_storage/mysql/as_mysql_wckey.c | 16 ++++++++++++ + 12 files changed, 145 insertions(+) + +diff --git a/src/plugins/accounting_storage/mysql/accounting_storage_mysql.c b/src/plugins/accounting_storage/mysql/accounting_storage_mysql.c +index 22854c4d5c..2a7a10f9c2 100644 +--- a/src/plugins/accounting_storage/mysql/accounting_storage_mysql.c ++++ b/src/plugins/accounting_storage/mysql/accounting_storage_mysql.c +@@ -1208,6 +1208,31 @@ extern void reset_mysql_conn(mysql_conn_t *mysql_conn) + list_flush(mysql_conn->update_list); + } + ++static int _find_unsafe_cluster(void *x, void *key) ++{ ++ char *cluster_name = x; ++ ++ return (cluster_name && strpbrk(cluster_name, "'\"\\`")); ++} ++ ++extern int as_mysql_validate_cluster_list(list_t *cluster_list) ++{ ++ char *cluster_name; ++ ++ if (!cluster_list || !list_count(cluster_list)) ++ return SLURM_SUCCESS; ++ ++ if ((cluster_name = list_find_first(cluster_list, ++ _find_unsafe_cluster, NULL))) { ++ error("%s: rejecting unsafe cluster name '%s'", ++ __func__, cluster_name); ++ errno = ESLURM_INVALID_CLUSTER_NAME; ++ return SLURM_ERROR; ++ } ++ ++ return SLURM_SUCCESS; ++} ++ + extern int create_cluster_assoc_table( + mysql_conn_t *mysql_conn, char *cluster_name) + { +diff --git a/src/plugins/accounting_storage/mysql/accounting_storage_mysql.h b/src/plugins/accounting_storage/mysql/accounting_storage_mysql.h +index e855768563..394eeb2f7e 100644 +--- a/src/plugins/accounting_storage/mysql/accounting_storage_mysql.h ++++ b/src/plugins/accounting_storage/mysql/accounting_storage_mysql.h +@@ -117,6 +117,13 @@ extern list_t *as_mysql_cluster_list; + extern list_t *as_mysql_total_cluster_list; + extern pthread_rwlock_t as_mysql_cluster_list_lock; + ++/* ++ * Return SLURM_ERROR if any name in cluster_list contains characters unsafe ++ * to interpolate into SQL (quotes, backslash, backtick); a NULL or empty ++ * list is accepted. ++ */ ++extern int as_mysql_validate_cluster_list(list_t *cluster_list); ++ + extern bool backup_dbd; + + typedef enum { +diff --git a/src/plugins/accounting_storage/mysql/as_mysql_archive.c b/src/plugins/accounting_storage/mysql/as_mysql_archive.c +index fc24e37859..b3d082c44e 100644 +--- a/src/plugins/accounting_storage/mysql/as_mysql_archive.c ++++ b/src/plugins/accounting_storage/mysql/as_mysql_archive.c +@@ -5588,6 +5588,13 @@ extern int as_mysql_jobacct_process_archive(mysql_conn_t *mysql_conn, + return SLURM_ERROR; + } + ++ if (arch_cond->job_cond) { ++ list_t *cluster_list = arch_cond->job_cond->cluster_list; ++ if (as_mysql_validate_cluster_list(cluster_list) != ++ SLURM_SUCCESS) ++ return SLURM_ERROR; ++ } ++ + if (arch_cond->job_cond && arch_cond->job_cond->cluster_list + && list_count(arch_cond->job_cond->cluster_list)) { + use_cluster_list = arch_cond->job_cond->cluster_list; +diff --git a/src/plugins/accounting_storage/mysql/as_mysql_assoc.c b/src/plugins/accounting_storage/mysql/as_mysql_assoc.c +index ff5bc86bb8..8a09ccbbce 100644 +--- a/src/plugins/accounting_storage/mysql/as_mysql_assoc.c ++++ b/src/plugins/accounting_storage/mysql/as_mysql_assoc.c +@@ -4062,6 +4062,10 @@ extern char *as_mysql_add_assocs_cond(mysql_conn_t *mysql_conn, uint32_t uid, + if (check_connection(mysql_conn) != SLURM_SUCCESS) + return NULL; + ++ if (as_mysql_validate_cluster_list(add_assoc->cluster_list) != ++ SLURM_SUCCESS) ++ return NULL; ++ + memset(&add_assoc_cond, 0, sizeof(add_assoc_cond)); + + if (!add_assoc->user_list && !add_assoc->assoc.parent_acct) +@@ -4212,6 +4216,10 @@ extern list_t *as_mysql_modify_assocs(mysql_conn_t *mysql_conn, uint32_t uid, + if (check_connection(mysql_conn) != SLURM_SUCCESS) + return NULL; + ++ if (as_mysql_validate_cluster_list(assoc_cond->cluster_list) != ++ SLURM_SUCCESS) ++ return NULL; ++ + memset(&user, 0, sizeof(slurmdb_user_rec_t)); + user.uid = uid; + +@@ -4448,6 +4456,10 @@ extern list_t *as_mysql_remove_assocs(mysql_conn_t *mysql_conn, uint32_t uid, + if (check_connection(mysql_conn) != SLURM_SUCCESS) + return NULL; + ++ if (as_mysql_validate_cluster_list(assoc_cond->cluster_list) != ++ SLURM_SUCCESS) ++ return NULL; ++ + memset(&user, 0, sizeof(slurmdb_user_rec_t)); + user.uid = uid; + +@@ -4620,6 +4632,10 @@ extern list_t *as_mysql_get_assocs(mysql_conn_t *mysql_conn, uid_t uid, + memset(&user, 0, sizeof(slurmdb_user_rec_t)); + user.uid = uid; + ++ if (as_mysql_validate_cluster_list(assoc_cond->cluster_list) != ++ SLURM_SUCCESS) ++ return NULL; ++ + if (slurm_conf.private_data & PRIVATE_DATA_USERS) { + if (!(is_admin = is_user_min_admin_level( + mysql_conn, uid, SLURMDB_ADMIN_OPERATOR))) { +@@ -4916,6 +4932,9 @@ extern int as_mysql_assoc_remove_default(mysql_conn_t *mysql_conn, + if (!(slurmdbd_conf->flags & DBD_CONF_FLAG_ALLOW_NO_DEF_ACCT)) + return ESLURM_NO_REMOVE_DEFAULT_ACCOUNT; + ++ if (as_mysql_validate_cluster_list(cluster_list) != SLURM_SUCCESS) ++ return SLURM_ERROR; ++ + slurmdb_init_assoc_rec(&assoc, 0); + assoc.acct = ""; + assoc.is_def = 1; +diff --git a/src/plugins/accounting_storage/mysql/as_mysql_cluster.c b/src/plugins/accounting_storage/mysql/as_mysql_cluster.c +index 3c3ec105f9..e4e7b6e721 100644 +--- a/src/plugins/accounting_storage/mysql/as_mysql_cluster.c ++++ b/src/plugins/accounting_storage/mysql/as_mysql_cluster.c +@@ -618,6 +618,10 @@ extern list_t *as_mysql_modify_clusters(mysql_conn_t *mysql_conn, uint32_t uid, + return NULL; + } + ++ if (as_mysql_validate_cluster_list(cluster_cond->cluster_list) != ++ SLURM_SUCCESS) ++ return NULL; ++ + /* force to only do non-deleted clusters */ + cluster_cond->with_deleted = 0; + _setup_cluster_cond_limits(cluster_cond, &extra); +@@ -849,6 +853,10 @@ extern list_t *as_mysql_remove_clusters(mysql_conn_t *mysql_conn, uint32_t uid, + return NULL; + } + ++ if (as_mysql_validate_cluster_list(cluster_cond->cluster_list) != ++ SLURM_SUCCESS) ++ return NULL; ++ + /* force to only do non-deleted clusters */ + cluster_cond->with_deleted = 0; + _setup_cluster_cond_limits(cluster_cond, &extra); +@@ -1012,6 +1020,10 @@ extern list_t *as_mysql_get_clusters(mysql_conn_t *mysql_conn, uid_t uid, + goto empty; + } + ++ if (as_mysql_validate_cluster_list(cluster_cond->cluster_list) != ++ SLURM_SUCCESS) ++ return NULL; ++ + _setup_cluster_cond_limits(cluster_cond, &extra); + + empty: +@@ -1190,6 +1202,10 @@ extern list_t *as_mysql_get_cluster_events(mysql_conn_t *mysql_conn, uint32_t ui + if (!event_cond) + goto empty; + ++ if (as_mysql_validate_cluster_list(event_cond->cluster_list) != ++ SLURM_SUCCESS) ++ return NULL; ++ + if (event_cond->cpus_min) { + if (extra) + xstrcat(extra, " && ("); +@@ -1589,6 +1605,10 @@ extern list_t *as_mysql_get_instances(mysql_conn_t *mysql_conn, uint32_t uid, + } + } + ++ if (instance_cond && ++ (as_mysql_validate_cluster_list(instance_cond->cluster_list) != ++ SLURM_SUCCESS)) ++ return NULL; + + /* determine cluster list */ + if (instance_cond && instance_cond->cluster_list && +diff --git a/src/plugins/accounting_storage/mysql/as_mysql_federation.c b/src/plugins/accounting_storage/mysql/as_mysql_federation.c +index 15a56bc615..367355edf9 100644 +--- a/src/plugins/accounting_storage/mysql/as_mysql_federation.c ++++ b/src/plugins/accounting_storage/mysql/as_mysql_federation.c +@@ -443,6 +443,10 @@ extern list_t *as_mysql_get_federations( + goto empty; + } + ++ if (as_mysql_validate_cluster_list(federation_cond->cluster_list) != ++ SLURM_SUCCESS) ++ return NULL; ++ + _setup_federation_cond_limits(federation_cond, &extra); + + empty: +@@ -531,6 +535,9 @@ extern list_t *as_mysql_modify_federations( + + /* force to only do non-deleted federations */ + fed_cond->with_deleted = 0; ++ if (as_mysql_validate_cluster_list(fed_cond->cluster_list) != ++ SLURM_SUCCESS) ++ return NULL; + _setup_federation_cond_limits(fed_cond, &extra); + _setup_federation_rec_limits(fed, &tmp_char1, &tmp_char2, &vals); + xfree(tmp_char1); +@@ -660,6 +667,9 @@ extern list_t *as_mysql_remove_federations(mysql_conn_t *mysql_conn, + + /* force to only do non-deleted federations */ + fed_cond->with_deleted = 0; ++ if (as_mysql_validate_cluster_list(fed_cond->cluster_list) != ++ SLURM_SUCCESS) ++ return NULL; + _setup_federation_cond_limits(fed_cond, &extra); + + if (!extra) { +diff --git a/src/plugins/accounting_storage/mysql/as_mysql_jobacct_process.c b/src/plugins/accounting_storage/mysql/as_mysql_jobacct_process.c +index 5b1576aad8..743780c05c 100644 +--- a/src/plugins/accounting_storage/mysql/as_mysql_jobacct_process.c ++++ b/src/plugins/accounting_storage/mysql/as_mysql_jobacct_process.c +@@ -1748,6 +1748,11 @@ extern list_t *as_mysql_jobacct_process_get_jobs(mysql_conn_t *mysql_conn, + memset(&user, 0, sizeof(slurmdb_user_rec_t)); + user.uid = uid; + ++ if (job_cond && ++ (as_mysql_validate_cluster_list(job_cond->cluster_list) != ++ SLURM_SUCCESS)) ++ return NULL; ++ + /* + * This clause must be kept in sync with the access check in + * _cluster_get_jobs(). +diff --git a/src/plugins/accounting_storage/mysql/as_mysql_problems.c b/src/plugins/accounting_storage/mysql/as_mysql_problems.c +index feaea30342..468e9d08c7 100644 +--- a/src/plugins/accounting_storage/mysql/as_mysql_problems.c ++++ b/src/plugins/accounting_storage/mysql/as_mysql_problems.c +@@ -119,6 +119,11 @@ extern int as_mysql_acct_no_assocs(mysql_conn_t *mysql_conn, + + xassert(ret_list); + ++ if (assoc_cond && ++ (as_mysql_validate_cluster_list(assoc_cond->cluster_list) != ++ SLURM_SUCCESS)) ++ return SLURM_ERROR; ++ + query = xstrdup_printf("select name from %s where deleted=0", + acct_table); + if (assoc_cond && +@@ -221,6 +226,11 @@ extern int as_mysql_acct_no_users(mysql_conn_t *mysql_conn, + + xassert(ret_list); + ++ if (assoc_cond && ++ (as_mysql_validate_cluster_list(assoc_cond->cluster_list) != ++ SLURM_SUCCESS)) ++ return SLURM_ERROR; ++ + _setup_assoc_cond_limits(assoc_cond, &extra, 0); + + /* if this changes you will need to edit the corresponding enum */ +@@ -326,6 +336,11 @@ extern int as_mysql_user_no_assocs_or_no_uid( + + xassert(ret_list); + ++ if (assoc_cond && ++ (as_mysql_validate_cluster_list(assoc_cond->cluster_list) != ++ SLURM_SUCCESS)) ++ return SLURM_ERROR; ++ + query = xstrdup_printf("select name from %s where deleted=0", + user_table); + if (assoc_cond && +diff --git a/src/plugins/accounting_storage/mysql/as_mysql_resource.c b/src/plugins/accounting_storage/mysql/as_mysql_resource.c +index 7af6fc2441..7ebffe1b35 100644 +--- a/src/plugins/accounting_storage/mysql/as_mysql_resource.c ++++ b/src/plugins/accounting_storage/mysql/as_mysql_resource.c +@@ -801,6 +801,11 @@ extern list_t *as_mysql_get_res(mysql_conn_t *mysql_conn, uid_t uid, + if (check_connection(mysql_conn) != SLURM_SUCCESS) + return NULL; + ++ if (res_cond && ++ (as_mysql_validate_cluster_list(res_cond->cluster_list) != ++ SLURM_SUCCESS)) ++ return NULL; ++ + _setup_res_cond(res_cond, &extra); + + xfree(tmp); +@@ -924,6 +929,10 @@ extern list_t *as_mysql_remove_res(mysql_conn_t *mysql_conn, uint32_t uid, + /* force to only do non-deleted server resources */ + res_cond->with_deleted = 0; + ++ if (as_mysql_validate_cluster_list(res_cond->cluster_list) != ++ SLURM_SUCCESS) ++ return NULL; ++ + _setup_res_cond(res_cond, &extra); + query_clusters = _setup_clus_res_cond(res_cond, &clus_extra); + +@@ -1102,6 +1111,10 @@ extern list_t *as_mysql_modify_res(mysql_conn_t *mysql_conn, uint32_t uid, + return NULL; + } + ++ if (as_mysql_validate_cluster_list(res_cond->cluster_list) != ++ SLURM_SUCCESS) ++ return NULL; ++ + _setup_res_limits(res, NULL, &tmp, &vals, 0, &send_update); + + xfree(tmp); +diff --git a/src/plugins/accounting_storage/mysql/as_mysql_resv.c b/src/plugins/accounting_storage/mysql/as_mysql_resv.c +index ae33a3f5fa..c82497db3f 100644 +--- a/src/plugins/accounting_storage/mysql/as_mysql_resv.c ++++ b/src/plugins/accounting_storage/mysql/as_mysql_resv.c +@@ -654,6 +654,10 @@ extern list_t *as_mysql_get_resvs(mysql_conn_t *mysql_conn, uid_t uid, + } + } + ++ if (as_mysql_validate_cluster_list(resv_cond->cluster_list) != ++ SLURM_SUCCESS) ++ return NULL; ++ + with_usage = resv_cond->with_usage; + + if (resv_cond->nodes) { +diff --git a/src/plugins/accounting_storage/mysql/as_mysql_txn.c b/src/plugins/accounting_storage/mysql/as_mysql_txn.c +index 4416010b5d..6b538bf6ff 100644 +--- a/src/plugins/accounting_storage/mysql/as_mysql_txn.c ++++ b/src/plugins/accounting_storage/mysql/as_mysql_txn.c +@@ -83,6 +83,10 @@ extern list_t *as_mysql_get_txn(mysql_conn_t *mysql_conn, uid_t uid, + if (!txn_cond) + goto empty; + ++ if (as_mysql_validate_cluster_list(txn_cond->cluster_list) != ++ SLURM_SUCCESS) ++ return NULL; ++ + /* handle query for associations first */ + if (txn_cond->acct_list && list_count(txn_cond->acct_list)) { + set = 0; +diff --git a/src/plugins/accounting_storage/mysql/as_mysql_wckey.c b/src/plugins/accounting_storage/mysql/as_mysql_wckey.c +index 95acc2cade..bfb6fbce6c 100644 +--- a/src/plugins/accounting_storage/mysql/as_mysql_wckey.c ++++ b/src/plugins/accounting_storage/mysql/as_mysql_wckey.c +@@ -857,6 +857,10 @@ extern char *as_mysql_add_wckeys_cond(mysql_conn_t *mysql_conn, uint32_t uid, + return NULL; + } + ++ if (as_mysql_validate_cluster_list(add_assoc->cluster_list) != ++ SLURM_SUCCESS) ++ return NULL; ++ + if (add_assoc->cluster_list && list_count(add_assoc->cluster_list)) + use_cluster_list = add_assoc->cluster_list; + else +@@ -934,6 +938,10 @@ extern list_t *as_mysql_modify_wckeys(mysql_conn_t *mysql_conn, + if (check_connection(mysql_conn) != SLURM_SUCCESS) + return NULL; + ++ if (as_mysql_validate_cluster_list(wckey_cond->cluster_list) != ++ SLURM_SUCCESS) ++ return NULL; ++ + if (!is_user_min_admin_level(mysql_conn, uid, SLURMDB_ADMIN_OPERATOR)) { + if (wckey_cond->user_list + && (list_count(wckey_cond->user_list) == 1)) { +@@ -1030,6 +1038,10 @@ extern list_t *as_mysql_remove_wckeys(mysql_conn_t *mysql_conn, + return NULL; + } + ++ if (as_mysql_validate_cluster_list(wckey_cond->cluster_list) != ++ SLURM_SUCCESS) ++ return NULL; ++ + (void) _setup_wckey_cond_limits(wckey_cond, &extra); + + empty: +@@ -1098,6 +1110,10 @@ extern list_t *as_mysql_get_wckeys(mysql_conn_t *mysql_conn, uid_t uid, + memset(&user, 0, sizeof(slurmdb_user_rec_t)); + user.uid = uid; + ++ if (as_mysql_validate_cluster_list(wckey_cond->cluster_list) != ++ SLURM_SUCCESS) ++ return NULL; ++ + if (slurm_conf.private_data & PRIVATE_DATA_USERS) { + if (!(is_admin = is_user_min_admin_level( + mysql_conn, uid, SLURMDB_ADMIN_OPERATOR))) { diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65139-2.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65139-2.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65139-2.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65139-2.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,156 @@ +From d08e49067498c70bd9922dd97a1bae071717710e Mon Sep 17 00:00:00 2001 +From: Tim McMullan +Date: Tue, 4 Aug 2026 14:52:27 -0400 +Subject: [PATCH] accounting_storage/mysql - Validate cluster names on the + remaining paths + +as_mysql_validate_cluster_list() only covered the cond fields, leaving the +usage queries and the add and runaway job paths to interpolate a caller +supplied name straight into a table identifier. Add a single name +validator and call it there. + +--- + .../mysql/accounting_storage_mysql.c | 12 ++++++++++++ + .../mysql/accounting_storage_mysql.h | 6 ++++++ + .../accounting_storage/mysql/as_mysql_assoc.c | 6 ++++++ + .../accounting_storage/mysql/as_mysql_cluster.c | 6 ++++++ + .../mysql/as_mysql_fix_runaway_jobs.c | 6 ++++++ + .../accounting_storage/mysql/as_mysql_usage.c | 8 ++++++++ + .../accounting_storage/mysql/as_mysql_wckey.c | 7 +++++++ + 7 files changed, 51 insertions(+) + +diff --git a/src/plugins/accounting_storage/mysql/accounting_storage_mysql.c b/src/plugins/accounting_storage/mysql/accounting_storage_mysql.c +index 2a7a10f9c2..028163e218 100644 +--- a/src/plugins/accounting_storage/mysql/accounting_storage_mysql.c ++++ b/src/plugins/accounting_storage/mysql/accounting_storage_mysql.c +@@ -1215,6 +1215,18 @@ static int _find_unsafe_cluster(void *x, void *key) + return (cluster_name && strpbrk(cluster_name, "'\"\\`")); + } + ++extern int as_mysql_validate_cluster_name(char *cluster_name) ++{ ++ if (_find_unsafe_cluster(cluster_name, NULL)) { ++ error("%s: rejecting unsafe cluster name '%s'", ++ __func__, cluster_name); ++ errno = ESLURM_INVALID_CLUSTER_NAME; ++ return SLURM_ERROR; ++ } ++ ++ return SLURM_SUCCESS; ++} ++ + extern int as_mysql_validate_cluster_list(list_t *cluster_list) + { + char *cluster_name; +diff --git a/src/plugins/accounting_storage/mysql/accounting_storage_mysql.h b/src/plugins/accounting_storage/mysql/accounting_storage_mysql.h +index 394eeb2f7e..f20c20efe7 100644 +--- a/src/plugins/accounting_storage/mysql/accounting_storage_mysql.h ++++ b/src/plugins/accounting_storage/mysql/accounting_storage_mysql.h +@@ -117,6 +117,12 @@ extern list_t *as_mysql_cluster_list; + extern list_t *as_mysql_total_cluster_list; + extern pthread_rwlock_t as_mysql_cluster_list_lock; + ++/* ++ * Return SLURM_ERROR if cluster_name contains characters unsafe to ++ * interpolate into SQL (quotes, backslash, backtick); NULL is accepted. ++ */ ++extern int as_mysql_validate_cluster_name(char *cluster_name); ++ + /* + * Return SLURM_ERROR if any name in cluster_list contains characters unsafe + * to interpolate into SQL (quotes, backslash, backtick); a NULL or empty +diff --git a/src/plugins/accounting_storage/mysql/as_mysql_assoc.c b/src/plugins/accounting_storage/mysql/as_mysql_assoc.c +index 8a09ccbbce..12d45a15b0 100644 +--- a/src/plugins/accounting_storage/mysql/as_mysql_assoc.c ++++ b/src/plugins/accounting_storage/mysql/as_mysql_assoc.c +@@ -3843,6 +3843,12 @@ extern int as_mysql_add_assocs(mysql_conn_t *mysql_conn, uint32_t uid, + continue; + } + ++ if (as_mysql_validate_cluster_name(object->cluster) != ++ SLURM_SUCCESS) { ++ rc = ESLURM_INVALID_CLUSTER_NAME; ++ continue; ++ } ++ + if (add_assoc_cond.is_coord && + !assoc_mgr_check_coord_qos(object->cluster, object->acct, + add_assoc_cond.user_name, +diff --git a/src/plugins/accounting_storage/mysql/as_mysql_cluster.c b/src/plugins/accounting_storage/mysql/as_mysql_cluster.c +index e4e7b6e721..c2afb72219 100644 +--- a/src/plugins/accounting_storage/mysql/as_mysql_cluster.c ++++ b/src/plugins/accounting_storage/mysql/as_mysql_cluster.c +@@ -312,6 +312,12 @@ extern int as_mysql_add_clusters(mysql_conn_t *mysql_conn, uint32_t uid, + list_remove(itr); + continue; + } ++ if (as_mysql_validate_cluster_name(object->name) != ++ SLURM_SUCCESS) { ++ rc = ESLURM_INVALID_CLUSTER_NAME; ++ list_delete_item(itr); ++ continue; ++ } + if ((object->flags != NO_VAL) && + (object->flags & CLUSTER_FLAG_EXT)) + external_cluster = true; +diff --git a/src/plugins/accounting_storage/mysql/as_mysql_fix_runaway_jobs.c b/src/plugins/accounting_storage/mysql/as_mysql_fix_runaway_jobs.c +index 247ce5f0bc..b30bc05acb 100644 +--- a/src/plugins/accounting_storage/mysql/as_mysql_fix_runaway_jobs.c ++++ b/src/plugins/accounting_storage/mysql/as_mysql_fix_runaway_jobs.c +@@ -137,6 +137,12 @@ extern int as_mysql_fix_runaway_jobs(mysql_conn_t *mysql_conn, uint32_t uid, + goto bail; + } + ++ if (as_mysql_validate_cluster_name(first_job->cluster) != ++ SLURM_SUCCESS) { ++ rc = ESLURM_INVALID_CLUSTER_NAME; ++ goto bail; ++ } ++ + /* + * Temporarily use mysql_conn->cluster_name for potentially non local + * cluster name, change back before return +diff --git a/src/plugins/accounting_storage/mysql/as_mysql_usage.c b/src/plugins/accounting_storage/mysql/as_mysql_usage.c +index f0b9c35c9b..6d564db3af 100644 +--- a/src/plugins/accounting_storage/mysql/as_mysql_usage.c ++++ b/src/plugins/accounting_storage/mysql/as_mysql_usage.c +@@ -564,6 +564,9 @@ static int _get_cluster_usage(mysql_conn_t *mysql_conn, uid_t uid, + return SLURM_ERROR; + } + ++ if (as_mysql_validate_cluster_name(cluster_rec->name) != SLURM_SUCCESS) ++ return SLURM_ERROR; ++ + if (set_usage_information(&my_usage_table, type, &start, &end) + != SLURM_SUCCESS) { + return SLURM_ERROR; +@@ -889,6 +892,11 @@ extern int as_mysql_get_usage(mysql_conn_t *mysql_conn, uid_t uid, + return SLURM_ERROR; + } + ++ if (as_mysql_validate_cluster_name(cluster_name) != SLURM_SUCCESS) { ++ xfree(id_str); ++ return SLURM_ERROR; ++ } ++ + if (slurm_conf.private_data & PRIVATE_DATA_USAGE) { + if (!(is_admin = is_user_min_admin_level( + mysql_conn, uid, SLURMDB_ADMIN_OPERATOR))) { +diff --git a/src/plugins/accounting_storage/mysql/as_mysql_wckey.c b/src/plugins/accounting_storage/mysql/as_mysql_wckey.c +index bfb6fbce6c..b758e53d9e 100644 +--- a/src/plugins/accounting_storage/mysql/as_mysql_wckey.c ++++ b/src/plugins/accounting_storage/mysql/as_mysql_wckey.c +@@ -708,6 +708,13 @@ extern int as_mysql_add_wckeys(mysql_conn_t *mysql_conn, uint32_t uid, + continue; + } + ++ if (as_mysql_validate_cluster_name(object->cluster) != ++ SLURM_SUCCESS) { ++ rc = ESLURM_INVALID_CLUSTER_NAME; ++ list_delete_item(itr); ++ continue; ++ } ++ + if (!added_user_list) + added_user_list = list_create(NULL); + if (!list_find_first(added_user_list, diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65139-3.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65139-3.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65139-3.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65139-3.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,34 @@ +From abf614994fdf312b9d307dcabbd06fe153224e4d Mon Sep 17 00:00:00 2001 +From: Tim McMullan +Date: Wed, 12 Aug 2026 09:49:35 -0400 +Subject: [PATCH] accounting_storage/mysql - Validate the cluster name on the + connection + +The cluster name is taken off the persistent connection init message +without any check and is used unquoted to build table names throughout the +plugin, so validating the condition structures alone still leaves it +reachable. Check it once when the connection is set up and refuse the +connection outright. + +--- + .../accounting_storage/mysql/accounting_storage_mysql.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/src/plugins/accounting_storage/mysql/accounting_storage_mysql.c b/src/plugins/accounting_storage/mysql/accounting_storage_mysql.c +index 028163e218..94c8c740ca 100644 +--- a/src/plugins/accounting_storage/mysql/accounting_storage_mysql.c ++++ b/src/plugins/accounting_storage/mysql/accounting_storage_mysql.c +@@ -3083,6 +3083,13 @@ extern void *acct_storage_p_get_connection( + + debug2("request new connection %d", rollback); + ++ /* ++ * The cluster name comes off the wire and is used unquoted to build ++ * table names, so it has to be checked before anything uses it. ++ */ ++ if (as_mysql_validate_cluster_name(cluster_name) != SLURM_SUCCESS) ++ return NULL; ++ + if (!(mysql_conn = create_mysql_conn( + conn_num, rollback, cluster_name))) { + fatal("couldn't get a mysql_conn"); diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65139-4.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65139-4.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65139-4.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65139-4.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,432 @@ +From 55edd5f85675f03b20a3e5f43e4e09004fa12086 Mon Sep 17 00:00:00 2001 +From: Tim McMullan +Date: Wed, 19 Aug 2026 12:52:07 -0400 +Subject: [PATCH] Validate accounting id lists as numeric when unpacking + +The numeric id lists reaching the mysql plugin are spliced into SQL, some +unquoted, so a non-numeric entry could inject. Validate each list as it is +unpacked rather than in every handler. + +The qos entries carry a leading +, - or = from the add, subtract and set +syntax, so the qos check permits those operators. + +Reviewed-by: Aron Xu +--- + src/common/slurmdb_pack.c | 236 +++++++++++++++++++------------------- + 1 file changed, 115 insertions(+), 121 deletions(-) + +diff --git a/src/common/slurmdb_pack.c b/src/common/slurmdb_pack.c +index 23571f35bf..635ed57449 100644 +--- a/src/common/slurmdb_pack.c ++++ b/src/common/slurmdb_pack.c +@@ -49,6 +49,73 @@ + #define KB_ADJ 1024 + #define MB_ADJ 1048576 + ++/* ++ * The numeric id lists below are spliced into SQL, some unquoted, so a ++ * non-numeric entry could inject. Validate every entry as it comes off the ++ * wire. _find_non_qos_id() tolerates the leading +/-/= that the add, ++ * subtract and set syntax carries on qos lists. ++ */ ++static int _find_non_numeric(void *x, void *key) ++{ ++ char *id = x; ++ ++ if (!id || !id[0]) ++ return 0; ++ ++ return id[strspn(id, "0123456789")]; ++} ++ ++static int _find_non_qos_id(void *x, void *key) ++{ ++ char *id = x; ++ ++ if (!id || !id[0]) ++ return 0; ++ ++ if ((id[0] == '+') || (id[0] == '-') || (id[0] == '=')) ++ id++; ++ ++ return (!id[0] || id[strspn(id, "0123456789")]); ++} ++ ++static int _unpack_id_list(list_t **id_list, buf_t *buffer, ++ uint16_t protocol_version) ++{ ++ char *id; ++ ++ if (slurm_unpack_list(id_list, safe_unpackstr_func, xfree_ptr, buffer, ++ protocol_version) != SLURM_SUCCESS) ++ return SLURM_ERROR; ++ ++ if (*id_list && ++ (id = list_find_first(*id_list, _find_non_numeric, NULL))) { ++ error("%s: rejecting non-numeric id '%s'", __func__, id); ++ FREE_NULL_LIST(*id_list); ++ return SLURM_ERROR; ++ } ++ ++ return SLURM_SUCCESS; ++} ++ ++static int _unpack_qos_id_list(list_t **id_list, buf_t *buffer, ++ uint16_t protocol_version) ++{ ++ char *id; ++ ++ if (slurm_unpack_list(id_list, safe_unpackstr_func, xfree_ptr, buffer, ++ protocol_version) != SLURM_SUCCESS) ++ return SLURM_ERROR; ++ ++ if (*id_list && ++ (id = list_find_first(*id_list, _find_non_qos_id, NULL))) { ++ error("%s: rejecting invalid qos id '%s'", __func__, id); ++ FREE_NULL_LIST(*id_list); ++ return SLURM_ERROR; ++ } ++ ++ return SLURM_SUCCESS; ++} ++ + static void _pack_slurmdb_stats(slurmdb_stats_t *stats, + uint16_t protocol_version, buf_t *buffer) + { +@@ -1561,11 +1628,8 @@ extern int slurmdb_unpack_assoc_rec_members(slurmdb_assoc_rec_t *object_ptr, + safe_unpackstr(&object_ptr->partition, buffer); + safe_unpack32(&object_ptr->priority, buffer); + +- if (slurm_unpack_list(&object_ptr->qos_list, +- safe_unpackstr_func, +- xfree_ptr, +- buffer, protocol_version) != +- SLURM_SUCCESS) ++ if (_unpack_qos_id_list(&object_ptr->qos_list, buffer, ++ protocol_version) != SLURM_SUCCESS) + goto unpack_error; + + safe_unpack32(&object_ptr->uid, buffer); +@@ -1617,11 +1681,8 @@ extern int slurmdb_unpack_assoc_rec_members(slurmdb_assoc_rec_t *object_ptr, + safe_unpackstr(&object_ptr->partition, buffer); + safe_unpack32(&object_ptr->priority, buffer); + +- if (slurm_unpack_list(&object_ptr->qos_list, +- safe_unpackstr_func, +- xfree_ptr, +- buffer, protocol_version) != +- SLURM_SUCCESS) ++ if (_unpack_qos_id_list(&object_ptr->qos_list, buffer, ++ protocol_version) != SLURM_SUCCESS) + goto unpack_error; + + safe_unpack32(&object_ptr->uid, buffer); +@@ -1673,11 +1734,8 @@ extern int slurmdb_unpack_assoc_rec_members(slurmdb_assoc_rec_t *object_ptr, + safe_unpackstr(&object_ptr->partition, buffer); + safe_unpack32(&object_ptr->priority, buffer); + +- if (slurm_unpack_list(&object_ptr->qos_list, +- safe_unpackstr_func, +- xfree_ptr, +- buffer, protocol_version) != +- SLURM_SUCCESS) ++ if (_unpack_qos_id_list(&object_ptr->qos_list, buffer, ++ protocol_version) != SLURM_SUCCESS) + goto unpack_error; + + safe_unpack32(&object_ptr->rgt, buffer); +@@ -2239,11 +2297,8 @@ extern int slurmdb_unpack_qos_rec(void **object, uint16_t protocol_version, + + unpack_bit_str_hex(&object_ptr->preempt_bitstr, buffer); + +- if (slurm_unpack_list(&object_ptr->preempt_list, +- safe_unpackstr_func, +- xfree_ptr, +- buffer, protocol_version) != +- SLURM_SUCCESS) ++ if (_unpack_qos_id_list(&object_ptr->preempt_list, buffer, ++ protocol_version) != SLURM_SUCCESS) + goto unpack_error; + + safe_unpack16(&object_ptr->preempt_mode, buffer); +@@ -2289,11 +2344,8 @@ extern int slurmdb_unpack_qos_rec(void **object, uint16_t protocol_version, + + unpack_bit_str_hex(&object_ptr->preempt_bitstr, buffer); + +- if (slurm_unpack_list(&object_ptr->preempt_list, +- safe_unpackstr_func, +- xfree_ptr, +- buffer, protocol_version) != +- SLURM_SUCCESS) ++ if (_unpack_qos_id_list(&object_ptr->preempt_list, buffer, ++ protocol_version) != SLURM_SUCCESS) + goto unpack_error; + + safe_unpack16(&object_ptr->preempt_mode, buffer); +@@ -2990,11 +3042,8 @@ extern int slurmdb_unpack_tres_cond(void **object, uint16_t protocol_version, + !list_count(object_ptr->format_list)) + FREE_NULL_LIST(object_ptr->format_list); + +- if (slurm_unpack_list(&object_ptr->id_list, +- safe_unpackstr_func, +- xfree_ptr, +- buffer, protocol_version) != +- SLURM_SUCCESS) ++ if (_unpack_id_list(&object_ptr->id_list, buffer, ++ protocol_version) != SLURM_SUCCESS) + goto unpack_error; + + if (slurm_unpack_list(&object_ptr->name_list, +@@ -3865,11 +3914,8 @@ extern int slurmdb_unpack_assoc_cond(void **object, + SLURM_SUCCESS) + goto unpack_error; + +- if (slurm_unpack_list(&object_ptr->def_qos_id_list, +- safe_unpackstr_func, +- xfree_ptr, +- buffer, protocol_version) != +- SLURM_SUCCESS) ++ if (_unpack_id_list(&object_ptr->def_qos_id_list, buffer, ++ protocol_version) != SLURM_SUCCESS) + goto unpack_error; + + safe_unpack32(&object_ptr->flags, buffer); +@@ -3884,11 +3930,8 @@ extern int slurmdb_unpack_assoc_cond(void **object, + !list_count(object_ptr->format_list)) + FREE_NULL_LIST(object_ptr->format_list); + +- if (slurm_unpack_list(&object_ptr->id_list, +- safe_unpackstr_func, +- xfree_ptr, +- buffer, protocol_version) != +- SLURM_SUCCESS) ++ if (_unpack_id_list(&object_ptr->id_list, buffer, ++ protocol_version) != SLURM_SUCCESS) + goto unpack_error; + + if (slurm_unpack_list(&object_ptr->partition_list, +@@ -3905,11 +3948,8 @@ extern int slurmdb_unpack_assoc_cond(void **object, + SLURM_SUCCESS) + goto unpack_error; + +- if (slurm_unpack_list(&object_ptr->qos_list, +- safe_unpackstr_func, +- xfree_ptr, +- buffer, protocol_version) != +- SLURM_SUCCESS) ++ if (_unpack_qos_id_list(&object_ptr->qos_list, buffer, ++ protocol_version) != SLURM_SUCCESS) + goto unpack_error; + + safe_unpack_time(&object_ptr->usage_end, buffer); +@@ -3937,11 +3977,8 @@ extern int slurmdb_unpack_assoc_cond(void **object, + SLURM_SUCCESS) + goto unpack_error; + +- if (slurm_unpack_list(&object_ptr->def_qos_id_list, +- safe_unpackstr_func, +- xfree_ptr, +- buffer, protocol_version) != +- SLURM_SUCCESS) ++ if (_unpack_id_list(&object_ptr->def_qos_id_list, buffer, ++ protocol_version) != SLURM_SUCCESS) + goto unpack_error; + + if (slurm_unpack_list(&object_ptr->format_list, +@@ -3954,11 +3991,8 @@ extern int slurmdb_unpack_assoc_cond(void **object, + !list_count(object_ptr->format_list)) + FREE_NULL_LIST(object_ptr->format_list); + +- if (slurm_unpack_list(&object_ptr->id_list, +- safe_unpackstr_func, +- xfree_ptr, +- buffer, protocol_version) != +- SLURM_SUCCESS) ++ if (_unpack_id_list(&object_ptr->id_list, buffer, ++ protocol_version) != SLURM_SUCCESS) + goto unpack_error; + + safe_unpack16(&tmp16, buffer); +@@ -3979,11 +4013,8 @@ extern int slurmdb_unpack_assoc_cond(void **object, + SLURM_SUCCESS) + goto unpack_error; + +- if (slurm_unpack_list(&object_ptr->qos_list, +- safe_unpackstr_func, +- xfree_ptr, +- buffer, protocol_version) != +- SLURM_SUCCESS) ++ if (_unpack_qos_id_list(&object_ptr->qos_list, buffer, ++ protocol_version) != SLURM_SUCCESS) + goto unpack_error; + + safe_unpack_time(&object_ptr->usage_end, buffer); +@@ -4113,11 +4144,8 @@ extern int slurmdb_unpack_event_cond(void **object, uint16_t protocol_version, + SLURM_SUCCESS) + goto unpack_error; + +- if (slurm_unpack_list(&object_ptr->reason_uid_list, +- safe_unpackstr_func, +- xfree_ptr, +- buffer, protocol_version) != +- SLURM_SUCCESS) ++ if (_unpack_id_list(&object_ptr->reason_uid_list, buffer, ++ protocol_version) != SLURM_SUCCESS) + goto unpack_error; + + if (slurm_unpack_list(&object_ptr->state_list, +@@ -4368,11 +4396,8 @@ extern int slurmdb_unpack_job_cond(void **object, uint16_t protocol_version, + SLURM_SUCCESS) + goto unpack_error; + +- if (slurm_unpack_list(&object_ptr->associd_list, +- safe_unpackstr_func, +- xfree_ptr, +- buffer, protocol_version) != +- SLURM_SUCCESS) ++ if (_unpack_id_list(&object_ptr->associd_list, buffer, ++ protocol_version) != SLURM_SUCCESS) + goto unpack_error; + + if (slurm_unpack_list(&object_ptr->cluster_list, +@@ -4406,11 +4431,8 @@ extern int slurmdb_unpack_job_cond(void **object, uint16_t protocol_version, + !list_count(object_ptr->format_list)) + FREE_NULL_LIST(object_ptr->format_list); + +- if (slurm_unpack_list(&object_ptr->groupid_list, +- safe_unpackstr_func, +- xfree_ptr, +- buffer, protocol_version) != +- SLURM_SUCCESS) ++ if (_unpack_id_list(&object_ptr->groupid_list, buffer, ++ protocol_version) != SLURM_SUCCESS) + goto unpack_error; + + if (slurm_unpack_list(&object_ptr->jobname_list, +@@ -4430,11 +4452,8 @@ extern int slurmdb_unpack_job_cond(void **object, uint16_t protocol_version, + SLURM_SUCCESS) + goto unpack_error; + +- if (slurm_unpack_list(&object_ptr->qos_list, +- safe_unpackstr_func, +- xfree_ptr, +- buffer, protocol_version) != +- SLURM_SUCCESS) ++ if (_unpack_id_list(&object_ptr->qos_list, buffer, ++ protocol_version) != SLURM_SUCCESS) + goto unpack_error; + + if (slurm_unpack_list(&object_ptr->reason_list, +@@ -4451,11 +4470,8 @@ extern int slurmdb_unpack_job_cond(void **object, uint16_t protocol_version, + SLURM_SUCCESS) + goto unpack_error; + +- if (slurm_unpack_list(&object_ptr->resvid_list, +- safe_unpackstr_func, +- xfree_ptr, +- buffer, protocol_version) != +- SLURM_SUCCESS) ++ if (_unpack_id_list(&object_ptr->resvid_list, buffer, ++ protocol_version) != SLURM_SUCCESS) + goto unpack_error; + + /* Leave not slurm_unpack_list as we check for the 0 job id */ +@@ -4498,12 +4514,8 @@ extern int slurmdb_unpack_job_cond(void **object, uint16_t protocol_version, + + safe_unpackstr(&object_ptr->used_nodes, buffer); + +- +- if (slurm_unpack_list(&object_ptr->userid_list, +- safe_unpackstr_func, +- xfree_ptr, +- buffer, protocol_version) != +- SLURM_SUCCESS) ++ if (_unpack_id_list(&object_ptr->userid_list, buffer, ++ protocol_version) != SLURM_SUCCESS) + goto unpack_error; + + if (slurm_unpack_list(&object_ptr->wckey_list, +@@ -5353,11 +5365,8 @@ extern int slurmdb_unpack_qos_cond(void **object, uint16_t protocol_version, + !list_count(object_ptr->format_list)) + FREE_NULL_LIST(object_ptr->format_list); + +- if (slurm_unpack_list(&object_ptr->id_list, +- safe_unpackstr_func, +- xfree_ptr, +- buffer, protocol_version) != +- SLURM_SUCCESS) ++ if (_unpack_id_list(&object_ptr->id_list, buffer, ++ protocol_version) != SLURM_SUCCESS) + goto unpack_error; + + if (slurm_unpack_list(&object_ptr->name_list, +@@ -5388,11 +5397,8 @@ extern int slurmdb_unpack_qos_cond(void **object, uint16_t protocol_version, + !list_count(object_ptr->format_list)) + FREE_NULL_LIST(object_ptr->format_list); + +- if (slurm_unpack_list(&object_ptr->id_list, +- safe_unpackstr_func, +- xfree_ptr, +- buffer, protocol_version) != +- SLURM_SUCCESS) ++ if (_unpack_id_list(&object_ptr->id_list, buffer, ++ protocol_version) != SLURM_SUCCESS) + goto unpack_error; + + if (slurm_unpack_list(&object_ptr->name_list, +@@ -5493,11 +5499,8 @@ extern int slurmdb_unpack_reservation_cond(void **object, + !list_count(object_ptr->format_list)) + FREE_NULL_LIST(object_ptr->format_list); + +- if (slurm_unpack_list(&object_ptr->id_list, +- safe_unpackstr_func, +- xfree_ptr, +- buffer, protocol_version) != +- SLURM_SUCCESS) ++ if (_unpack_id_list(&object_ptr->id_list, buffer, ++ protocol_version) != SLURM_SUCCESS) + goto unpack_error; + + if (slurm_unpack_list(&object_ptr->name_list, +@@ -5721,11 +5724,8 @@ extern int slurmdb_unpack_res_cond(void **object, uint16_t protocol_version, + !list_count(object_ptr->format_list)) + FREE_NULL_LIST(object_ptr->format_list); + +- if (slurm_unpack_list(&object_ptr->id_list, +- safe_unpackstr_func, +- xfree_ptr, +- buffer, protocol_version) != +- SLURM_SUCCESS) ++ if (_unpack_id_list(&object_ptr->id_list, buffer, ++ protocol_version) != SLURM_SUCCESS) + goto unpack_error; + + if (slurm_unpack_list(&object_ptr->manager_list, +@@ -5887,11 +5887,8 @@ extern int slurmdb_unpack_txn_cond(void **object, uint16_t protocol_version, + !list_count(object_ptr->format_list)) + FREE_NULL_LIST(object_ptr->format_list); + +- if (slurm_unpack_list(&object_ptr->id_list, +- safe_unpackstr_func, +- xfree_ptr, +- buffer, protocol_version) != +- SLURM_SUCCESS) ++ if (_unpack_id_list(&object_ptr->id_list, buffer, ++ protocol_version) != SLURM_SUCCESS) + goto unpack_error; + + if (slurm_unpack_list(&object_ptr->info_list, +@@ -6012,11 +6009,8 @@ extern int slurmdb_unpack_wckey_cond(void **object, uint16_t protocol_version, + !list_count(object_ptr->format_list)) + FREE_NULL_LIST(object_ptr->format_list); + +- if (slurm_unpack_list(&object_ptr->id_list, +- safe_unpackstr_func, +- xfree_ptr, +- buffer, protocol_version) != +- SLURM_SUCCESS) ++ if (_unpack_id_list(&object_ptr->id_list, buffer, ++ protocol_version) != SLURM_SUCCESS) + goto unpack_error; + + if (slurm_unpack_list(&object_ptr->name_list, diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65139-5.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65139-5.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65139-5.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65139-5.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,57 @@ +From 9db8b711a8ab8c0ed1ecc7bdb4ecb53a990d8ffe Mon Sep 17 00:00:00 2001 +From: Tim McMullan +Date: Wed, 19 Aug 2026 17:35:41 -0400 +Subject: [PATCH] Testsuite - Use numeric ids in the slurmdb pack tests + +pack_assoc_rec, pack_assoc_rec_with_usage and pack_event_cond put names in +qos_list and reason_uid_list, which are now validated as numeric on unpack. +Use numeric values so the round trip tests pass. + +(cherry picked from commit 9db8b711a8ab8c0ed1ecc7bdb4ecb53a990d8ffe) +--- + .../slurm_unit/common/slurmdb_pack/pack_assoc_rec-test.c | 2 +- + .../common/slurmdb_pack/pack_assoc_rec_with_usage-test.c | 2 +- + .../slurm_unit/common/slurmdb_pack/pack_event_cond-test.c | 4 ++-- + 3 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/testsuite/slurm_unit/common/slurmdb_pack/pack_assoc_rec-test.c b/testsuite/slurm_unit/common/slurmdb_pack/pack_assoc_rec-test.c +index 03e4124c6a..9f0e21209a 100644 +--- a/testsuite/slurm_unit/common/slurmdb_pack/pack_assoc_rec-test.c ++++ b/testsuite/slurm_unit/common/slurmdb_pack/pack_assoc_rec-test.c +@@ -96,7 +96,7 @@ START_TEST(pack_1702_assoc_rec) + slurmdb_accounting_rec_t *art = xmalloc(sizeof(slurmdb_accounting_rec_t)); + + art->id = 66; +- char *qos = xstrdup("Eusebius"); ++ char *qos = xstrdup("5"); + + pack_ar->accounting_list = list_create(slurmdb_destroy_accounting_rec); + pack_ar->acct = xstrdup("Socrates"); +diff --git a/testsuite/slurm_unit/common/slurmdb_pack/pack_assoc_rec_with_usage-test.c b/testsuite/slurm_unit/common/slurmdb_pack/pack_assoc_rec_with_usage-test.c +index eaaab65cb7..103f1da8ae 100644 +--- a/testsuite/slurm_unit/common/slurmdb_pack/pack_assoc_rec_with_usage-test.c ++++ b/testsuite/slurm_unit/common/slurmdb_pack/pack_assoc_rec_with_usage-test.c +@@ -71,7 +71,7 @@ START_TEST(pack_1702_assoc_rec) + slurmdb_accounting_rec_t *art = xmalloc(sizeof(slurmdb_accounting_rec_t)); + + art->id = 66; +- char *qos = xstrdup("Eusebius"); ++ char *qos = xstrdup("5"); + + pack_arwu->accounting_list = list_create(slurmdb_destroy_accounting_rec); + pack_arwu->acct = xstrdup("Socrates"); +diff --git a/testsuite/slurm_unit/common/slurmdb_pack/pack_event_cond-test.c b/testsuite/slurm_unit/common/slurmdb_pack/pack_event_cond-test.c +index 17704beeef..e219a63f9b 100644 +--- a/testsuite/slurm_unit/common/slurmdb_pack/pack_event_cond-test.c ++++ b/testsuite/slurm_unit/common/slurmdb_pack/pack_event_cond-test.c +@@ -55,8 +55,8 @@ static void _init_event_cond(slurmdb_event_cond_t *pack) + list_append(pack->reason_list, "reason2"); + + pack->reason_uid_list = list_create(xfree_ptr); +- list_append(pack->reason_uid_list, "uid1"); +- list_append(pack->reason_uid_list, "uid2"); ++ list_append(pack->reason_uid_list, "1001"); ++ list_append(pack->reason_uid_list, "1002"); + + pack->state_list = list_create(xfree_ptr); + list_append(pack->state_list, "state1"); diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65140-1.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65140-1.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65140-1.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65140-1.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,153 @@ +From c8dcba55c4c82ea00528079ce3911e30d7d3c971 Mon Sep 17 00:00:00 2001 +From: Tim McMullan +Date: Tue, 11 Aug 2026 14:25:37 -0400 +Subject: [PATCH] accounting_storage/mysql - Don't rewrite admin_level on + existing user + +--- + .../accounting_storage/mysql/as_mysql_user.c | 73 +++++++++++++++---- + 1 file changed, 60 insertions(+), 13 deletions(-) + +diff --git a/src/plugins/accounting_storage/mysql/as_mysql_user.c b/src/plugins/accounting_storage/mysql/as_mysql_user.c +index 5588ae8aac..56a3df31ff 100644 +--- a/src/plugins/accounting_storage/mysql/as_mysql_user.c ++++ b/src/plugins/accounting_storage/mysql/as_mysql_user.c +@@ -524,6 +524,28 @@ static int _foreach_add_user(void *x, void *arg) + return 0; + } + ++/* Return true if name is not already an existing, non-deleted user. */ ++static bool _admin_level_applies(mysql_conn_t *mysql_conn, char *name) ++{ ++ char *query = NULL; ++ MYSQL_RES *result = NULL; ++ bool applies; ++ ++ query = xstrdup_printf( ++ "select name from %s where deleted=0 and name='%s';", ++ user_table, name); ++ ++ result = mysql_db_query_ret(mysql_conn, query, 0); ++ xfree(query); ++ if (!result) ++ return false; ++ ++ applies = !mysql_num_rows(result); ++ mysql_free_result(result); ++ ++ return applies; ++} ++ + extern int as_mysql_add_users(mysql_conn_t *mysql_conn, uint32_t uid, + list_t *user_list) + { +@@ -534,7 +556,7 @@ extern int as_mysql_add_users(mysql_conn_t *mysql_conn, uint32_t uid, + char *txn_query_pos = NULL; + time_t now = time(NULL); + char *user_name = NULL; +- char *extra = NULL, *tmp_extra = NULL; ++ char *extra = NULL, *audit_extra = NULL, *tmp_extra = NULL; + int affect_rows = 0; + list_t *assoc_list; + list_t *wckey_list; +@@ -598,17 +620,30 @@ extern int as_mysql_add_users(mysql_conn_t *mysql_conn, uint32_t uid, + } + xstrcat(cols, ", admin_level"); + xstrfmtcat(vals, ", %u", object->admin_level); +- xstrfmtcat(extra, ", admin_level=%u", +- object->admin_level); +- } else +- xstrfmtcat(extra, ", admin_level=%u", +- SLURMDB_ADMIN_NONE); ++ xstrfmtcat(extra, ++ ", admin_level=IF(deleted=1, " ++ "VALUES(admin_level), admin_level)"); ++ /* ++ * The admin_level is only applied to a new or deleted ++ * user, so only record it as changed in those cases. ++ */ ++ if (_admin_level_applies(mysql_conn, object->name)) ++ xstrfmtcat(audit_extra, ", admin_level=%u", ++ object->admin_level); ++ } else { ++ xstrcat(cols, ", admin_level"); ++ xstrfmtcat(vals, ", %u", SLURMDB_ADMIN_NONE); ++ xstrfmtcat(extra, ++ ", admin_level=IF(deleted=1, " ++ "VALUES(admin_level), admin_level)"); ++ } + + query = xstrdup_printf( + "insert into %s (%s) values (%s) " +- "on duplicate key update name=VALUES(name), deleted=0, mod_time=%ld %s;", ++ "on duplicate key update name=VALUES(name)%s, " ++ "deleted=0, mod_time=%ld;", + user_table, cols, vals, +- (long)now, extra); ++ extra, (long) now); + xfree(cols); + xfree(vals); + +@@ -617,6 +652,7 @@ extern int as_mysql_add_users(mysql_conn_t *mysql_conn, uint32_t uid, + if (rc != SLURM_SUCCESS) { + error("Couldn't add user %s", object->name); + xfree(extra); ++ xfree(audit_extra); + continue; + } + +@@ -624,6 +660,7 @@ extern int as_mysql_add_users(mysql_conn_t *mysql_conn, uint32_t uid, + if (!affect_rows) { + debug("nothing changed"); + xfree(extra); ++ xfree(audit_extra); + continue; + } + +@@ -643,21 +680,30 @@ extern int as_mysql_add_users(mysql_conn_t *mysql_conn, uint32_t uid, + rc = _get_user_coords(mysql_conn, object); + } + +- if (rc != SLURM_SUCCESS) ++ if (rc != SLURM_SUCCESS) { ++ xfree(extra); ++ xfree(audit_extra); + continue; ++ } + + if (addto_update_list(mysql_conn->update_list, SLURMDB_ADD_USER, + object) == SLURM_SUCCESS) + list_remove(itr); + +- /* we always have a ', ' as the first 2 chars */ +- tmp_extra = slurm_add_slash_to_quotes(extra+2); ++ /* ++ * audit_extra is only set when the caller explicitly requested ++ * an admin_level; otherwise the txn info is left empty rather ++ * than recording the SQL fragment from extra. We always have ++ * a ', ' as the first 2 chars. ++ */ ++ if (audit_extra) ++ tmp_extra = slurm_add_slash_to_quotes(audit_extra + 2); + + if (txn_query) + xstrfmtcatat(txn_query, &txn_query_pos, + ", (%ld, %u, '%s', '%s', '%s')", + (long)now, DBD_ADD_USERS, object->name, +- user_name, tmp_extra); ++ user_name, tmp_extra ? tmp_extra : ""); + else + xstrfmtcatat(txn_query, &txn_query_pos, + "insert into %s " +@@ -665,9 +711,10 @@ extern int as_mysql_add_users(mysql_conn_t *mysql_conn, uint32_t uid, + "values (%ld, %u, '%s', '%s', '%s')", + txn_table, + (long)now, DBD_ADD_USERS, object->name, +- user_name, tmp_extra); ++ user_name, tmp_extra ? tmp_extra : ""); + xfree(tmp_extra); + xfree(extra); ++ xfree(audit_extra); + if (object->assoc_list) + list_transfer(assoc_list, object->assoc_list); + diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65140-2.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65140-2.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65140-2.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65140-2.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,74 @@ +From 77d9338ae17e89ff129992fd8146bd821b1deedb Mon Sep 17 00:00:00 2001 +From: Tim McMullan +Date: Tue, 11 Aug 2026 14:25:37 -0400 +Subject: [PATCH] accounting_storage/mysql - Don't rewrite admin_level (cond) + +(cherry picked from commit 77d9338ae17e89ff129992fd8146bd821b1deedb) +--- + .../accounting_storage/mysql/as_mysql_user.c | 18 +++++++++++------- + 1 file changed, 11 insertions(+), 7 deletions(-) + +diff --git a/src/plugins/accounting_storage/mysql/as_mysql_user.c b/src/plugins/accounting_storage/mysql/as_mysql_user.c +index 56a3df31ff..77f6e7cbc6 100644 +--- a/src/plugins/accounting_storage/mysql/as_mysql_user.c ++++ b/src/plugins/accounting_storage/mysql/as_mysql_user.c +@@ -43,6 +43,7 @@ + typedef struct { + list_t *acct_list; /* for coords, a list of just char * instead of + * slurmdb_coord_rec_t */ ++ bool admin_set; + char *coord_query; + char *coord_query_pos; + mysql_conn_t *mysql_conn; +@@ -423,7 +424,7 @@ static int _foreach_add_user(void *x, void *arg) + char *name = x; + add_user_cond_t *add_user_cond = arg; + slurmdb_user_rec_t *object, check_object; +- char *extra, *tmp_extra; ++ char *extra = NULL, *tmp_extra = NULL; + int rc; + char *query; + +@@ -449,9 +450,9 @@ static int _foreach_add_user(void *x, void *arg) + add_user_cond->user_in->coord_accts); + + query = xstrdup_printf( +- "insert into %s (creation_time, mod_time, name, admin_level) values (%ld, %ld, '%s', %u) on duplicate key update deleted=0, mod_time=VALUES(mod_time), admin_level=VALUES(admin_level);", +- user_table, add_user_cond->now, add_user_cond->now, +- object->name, object->admin_level); ++ "insert into %s (creation_time, mod_time, name, admin_level) values (%ld, %ld, '%s', %u) on duplicate key update name=VALUES(name), admin_level=IF(deleted=1, VALUES(admin_level), admin_level), deleted=0, mod_time=VALUES(mod_time);", ++ user_table, add_user_cond->now, add_user_cond->now, object->name, ++ object->admin_level); + + DB_DEBUG(DB_ASSOC, add_user_cond->mysql_conn->conn, "query:\n%s", + query); +@@ -484,8 +485,10 @@ static int _foreach_add_user(void *x, void *arg) + return -1; + } + +- extra = xstrdup_printf("admin_level=%u", object->admin_level); +- tmp_extra = slurm_add_slash_to_quotes(extra); ++ if (add_user_cond->admin_set) { ++ extra = xstrdup_printf("admin_level=%u", object->admin_level); ++ tmp_extra = slurm_add_slash_to_quotes(extra); ++ } + + if (!add_user_cond->txn_query) + xstrfmtcatat(add_user_cond->txn_query, +@@ -501,7 +504,7 @@ static int _foreach_add_user(void *x, void *arg) + &add_user_cond->txn_query_pos, + "(%ld, %u, '%s', '%s', '%s')", + add_user_cond->now, DBD_ADD_USERS, name, +- add_user_cond->user_name, tmp_extra); ++ add_user_cond->user_name, tmp_extra ? tmp_extra : ""); + xfree(tmp_extra); + xfree(extra); + +@@ -807,6 +810,7 @@ extern char *as_mysql_add_users_cond(mysql_conn_t *mysql_conn, uint32_t uid, + admin_set = true; + + memset(&add_user_cond, 0, sizeof(add_user_cond)); ++ add_user_cond.admin_set = admin_set; + add_user_cond.user_in = user; + add_user_cond.mysql_conn = mysql_conn; + add_user_cond.now = time(NULL); diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65140-3.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65140-3.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65140-3.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65140-3.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,55 @@ +From 6312dd2d02be625b0c491bf236d2d67109967d9a Mon Sep 17 00:00:00 2001 +From: Tim McMullan +Date: Tue, 11 Aug 2026 14:25:37 -0400 +Subject: [PATCH] accounting_storage/mysql - Require SUPER_USER to grant + SUPER_USER + +(cherry picked from commit 6312dd2d02be625b0c491bf236d2d67109967d9a) +--- + src/plugins/accounting_storage/mysql/as_mysql_user.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/src/plugins/accounting_storage/mysql/as_mysql_user.c b/src/plugins/accounting_storage/mysql/as_mysql_user.c +index 77f6e7cbc6..2c70471439 100644 +--- a/src/plugins/accounting_storage/mysql/as_mysql_user.c ++++ b/src/plugins/accounting_storage/mysql/as_mysql_user.c +@@ -564,6 +564,7 @@ extern int as_mysql_add_users(mysql_conn_t *mysql_conn, uint32_t uid, + list_t *assoc_list; + list_t *wckey_list; + bool is_admin = false; ++ bool is_super_user = false; + + if (check_connection(mysql_conn) != SLURM_SUCCESS) + return ESLURM_DB_CONNECTION; +@@ -591,6 +592,8 @@ extern int as_mysql_add_users(mysql_conn_t *mysql_conn, uint32_t uid, + */ + } else { + is_admin = true; ++ is_super_user = is_user_min_admin_level( ++ mysql_conn, uid, SLURMDB_ADMIN_SUPER_USER); + } + + if (!user_list || !list_count(user_list)) { +@@ -621,6 +624,13 @@ extern int as_mysql_add_users(mysql_conn_t *mysql_conn, uint32_t uid, + rc = ESLURM_ACCESS_DENIED; + break; + } ++ if ((object->admin_level >= ++ SLURMDB_ADMIN_SUPER_USER) && ++ !is_super_user) { ++ error("Only Administrators can add an Administrator"); ++ rc = ESLURM_ACCESS_DENIED; ++ break; ++ } + xstrcat(cols, ", admin_level"); + xstrfmtcat(vals, ", %u", object->admin_level); + xstrfmtcat(extra, +@@ -725,6 +735,8 @@ extern int as_mysql_add_users(mysql_conn_t *mysql_conn, uint32_t uid, + list_transfer(wckey_list, object->wckey_list); + } + list_iterator_destroy(itr); ++ xfree(cols); ++ xfree(vals); + xfree(user_name); + + if (rc == SLURM_SUCCESS) { diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65140-4.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65140-4.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65140-4.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65140-4.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,32 @@ +From a82ec42d52b5ff092a7cc620332449ea6670c80b Mon Sep 17 00:00:00 2001 +From: Tim McMullan +Date: Tue, 11 Aug 2026 14:25:37 -0400 +Subject: [PATCH] accounting_storage/mysql - Require SUPER_USER in + as_mysql_add_users_cond + +(cherry picked from commit a82ec42d52b5ff092a7cc620332449ea6670c80b) +--- + src/plugins/accounting_storage/mysql/as_mysql_user.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/src/plugins/accounting_storage/mysql/as_mysql_user.c b/src/plugins/accounting_storage/mysql/as_mysql_user.c +index 2c70471439..ad70f47732 100644 +--- a/src/plugins/accounting_storage/mysql/as_mysql_user.c ++++ b/src/plugins/accounting_storage/mysql/as_mysql_user.c +@@ -816,6 +816,16 @@ extern char *as_mysql_add_users_cond(mysql_conn_t *mysql_conn, uint32_t uid, + */ + } + ++ if ((user->admin_level >= SLURMDB_ADMIN_SUPER_USER) && ++ !is_user_min_admin_level(mysql_conn, uid, ++ SLURMDB_ADMIN_SUPER_USER)) { ++ ret_str = ++ xstrdup("Only Administrators can add an Administrator"); ++ error("%s", ret_str); ++ errno = ESLURM_ACCESS_DENIED; ++ return ret_str; ++ } ++ + if (user->admin_level == SLURMDB_ADMIN_NOTSET) + user->admin_level = SLURMDB_ADMIN_NONE; + else diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65140-5.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65140-5.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65140-5.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65140-5.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,109 @@ +From 56ad4b7574a008958af5c3830ecdc5c224383254 Mon Sep 17 00:00:00 2001 +From: Tim McMullan +Date: Tue, 11 Aug 2026 14:25:37 -0400 +Subject: [PATCH] accounting_storage/mysql - Require SUPER_USER to remove an + Administrator + +(cherry picked from commit 56ad4b7574a008958af5c3830ecdc5c224383254) + +Reviewed-by: Aron Xu +--- + .../accounting_storage/mysql/as_mysql_user.c | 71 +++++++++++++++++++ + 1 file changed, 71 insertions(+) + +diff --git a/src/plugins/accounting_storage/mysql/as_mysql_user.c b/src/plugins/accounting_storage/mysql/as_mysql_user.c +index ad70f47732..bebccc6071 100644 +--- a/src/plugins/accounting_storage/mysql/as_mysql_user.c ++++ b/src/plugins/accounting_storage/mysql/as_mysql_user.c +@@ -58,6 +58,11 @@ typedef struct { + char *user_name; + } add_user_cond_t; + ++typedef struct { ++ char *query; ++ char *query_pos; ++} create_string_t; ++ + static int _change_user_name(mysql_conn_t *mysql_conn, slurmdb_user_rec_t *user) + { + int rc = SLURM_SUCCESS; +@@ -1330,6 +1335,56 @@ static bool _is_coord_over_all_accts(mysql_conn_t *mysql_conn, + return has_access; + } + ++static int _foreach_quoted_name(void *x, void *arg) ++{ ++ char *name = x; ++ create_string_t *create_string = arg; ++ ++ if (!name[0]) ++ return 0; ++ ++ xstrfmtcatat(create_string->query, &create_string->query_pos, ++ "%s'%s'", ++ create_string->query ? ", " : "", name); ++ return 0; ++} ++ ++/* ++ * Return ESLURM_ACCESS_DENIED if any name in name_list is an Administrator. ++ */ ++static int _check_for_admins(mysql_conn_t *mysql_conn, list_t *name_list) ++{ ++ create_string_t create_string = { 0 }; ++ char *query = NULL; ++ MYSQL_RES *result = NULL; ++ int rc = SLURM_SUCCESS; ++ ++ (void) list_for_each_ro(name_list, _foreach_quoted_name, ++ &create_string); ++ ++ if (!create_string.query) ++ return SLURM_SUCCESS; ++ ++ query = xstrdup_printf( ++ "select name from %s " ++ "where deleted=0 and admin_level>=%u and name in (%s);", ++ user_table, SLURMDB_ADMIN_SUPER_USER, create_string.query); ++ xfree(create_string.query); ++ ++ result = mysql_db_query_ret(mysql_conn, query, 0); ++ xfree(query); ++ if (!result) ++ return ESLURM_ACCESS_DENIED; ++ ++ if (mysql_num_rows(result)) { ++ error("Only Administrators can remove an Administrator"); ++ rc = ESLURM_ACCESS_DENIED; ++ } ++ mysql_free_result(result); ++ ++ return rc; ++} ++ + extern list_t *as_mysql_remove_users(mysql_conn_t *mysql_conn, uint32_t uid, + slurmdb_user_cond_t *user_cond) + { +@@ -1452,6 +1507,22 @@ no_user_table: + } + xfree(query); + ++ /* ++ * ret_list is also filled in from the default account and wckey ++ * conditions, which never look at the user_table, so the admin_level ++ * has to be checked here on the full list. ++ */ ++ if (!is_user_min_admin_level(mysql_conn, uid, ++ SLURMDB_ADMIN_SUPER_USER)) { ++ rc = _check_for_admins(mysql_conn, ret_list); ++ ++ if (rc != SLURM_SUCCESS) { ++ FREE_NULL_LIST(ret_list); ++ errno = rc; ++ return NULL; ++ } ++ } ++ + memset(&user_coord_cond, 0, sizeof(slurmdb_user_cond_t)); + memset(&assoc_cond, 0, sizeof(slurmdb_assoc_cond_t)); + /* we do not need to free the objects we put in here since diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65140-6.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65140-6.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65140-6.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65140-6.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,64 @@ +From f1101a696f7f7de4602ddd12941f2cd6f1ad5799 Mon Sep 17 00:00:00 2001 +From: Tim McMullan +Date: Tue, 11 Aug 2026 18:21:45 -0400 +Subject: [PATCH] accounting_storage/mysql - Require SUPER_USER to rename an + Administrator + +(cherry picked from commit f1101a696f7f7de4602ddd12941f2cd6f1ad5799) +--- + .../accounting_storage/mysql/as_mysql_user.c | 22 ++++++++++++++++++- + 1 file changed, 21 insertions(+), 1 deletion(-) + +diff --git a/src/plugins/accounting_storage/mysql/as_mysql_user.c b/src/plugins/accounting_storage/mysql/as_mysql_user.c +index bebccc6071..27f210a7db 100644 +--- a/src/plugins/accounting_storage/mysql/as_mysql_user.c ++++ b/src/plugins/accounting_storage/mysql/as_mysql_user.c +@@ -1076,6 +1076,7 @@ extern list_t *as_mysql_modify_users(mysql_conn_t *mysql_conn, uint32_t uid, + int set = 0; + MYSQL_RES *result = NULL; + MYSQL_ROW row; ++ bool is_super_user = false; + + if (!user_cond || !user) { + error("we need something to change"); +@@ -1126,7 +1127,7 @@ extern list_t *as_mysql_modify_users(mysql_conn_t *mysql_conn, uint32_t uid, + } + + query = xstrdup_printf( +- "select distinct name from %s where deleted=0 %s;", ++ "select distinct name, admin_level from %s where deleted=0 %s;", + user_table, extra); + xfree(extra); + if (!(result = mysql_db_query_ret( +@@ -1136,12 +1137,31 @@ extern list_t *as_mysql_modify_users(mysql_conn_t *mysql_conn, uint32_t uid, + return NULL; + } + ++ if (user->name) ++ is_super_user = is_user_min_admin_level( ++ mysql_conn, uid, SLURMDB_ADMIN_SUPER_USER); ++ + if (!ret_list) + ret_list = list_create(xfree_ptr); + while ((row = mysql_fetch_row(result))) { + slurmdb_user_rec_t *user_rec = NULL; + + object = row[0]; ++ /* ++ * A rename carries the admin_level over to the new name, so ++ * it has to be checked before _change_user_name() below. ++ */ ++ if (user->name && !is_super_user && ++ (slurm_atoul(row[1]) >= SLURMDB_ADMIN_SUPER_USER)) { ++ error("Only Administrators can rename an Administrator"); ++ mysql_free_result(result); ++ xfree(name_char); ++ xfree(query); ++ xfree(vals); ++ FREE_NULL_LIST(ret_list); ++ errno = ESLURM_ACCESS_DENIED; ++ return NULL; ++ } + slurm_addto_char_list(ret_list, object); + if (!name_char) + xstrfmtcat(name_char, "(name='%s'", object); diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65165-1.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65165-1.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65165-1.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65165-1.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,90 @@ +From 63d9bec37114293f0eb65151ec0c0cb116bf3094 Mon Sep 17 00:00:00 2001 +From: Dominik Bartkiewicz +Date: Wed, 2 Sep 2026 11:03:58 -0400 +Subject: [PATCH] Fix step node count for arbitrary distribution + +An arbitrary step whose node list matched the job's req_nodes, or +that requested all nodes, skipped the node list check. Its node +count then came from the request while its task layout came from +the list, leaving step_layout->node_cnt disagreeing with the step's +node bitmap. Take the count from the list in both cases. + +Pin max_nodes to that same count as well. Without it the later +cpu_count block can raise min_nodes above the list's node count and +the node top-up loops then extend the step past the nodes the list +names, which is how the two counts came apart. + +Reviewed-by: Aron Xu +--- + src/stepmgr/stepmgr.c | 36 ++++++++++++++++++++++++++---------- + 1 file changed, 26 insertions(+), 10 deletions(-) + +diff --git a/src/stepmgr/stepmgr.c b/src/stepmgr/stepmgr.c +index 89cd0a6ba2..99b6f88442 100644 +--- a/src/stepmgr/stepmgr.c ++++ b/src/stepmgr/stepmgr.c +@@ -1433,7 +1433,10 @@ static bitstr_t *_pick_step_nodes(job_record_t *job_ptr, + return NULL; + } + +- if (step_spec->min_nodes == INFINITE) { /* use all nodes */ ++ if ((step_spec->min_nodes == INFINITE) && ++ (!step_spec->node_list || ++ ((step_spec->task_dist & SLURM_DIST_STATE_BASE) != ++ SLURM_DIST_ARBITRARY))) { /* use all nodes */ + if ((step_spec->num_tasks == NO_VAL) && nodes_avail && + !(step_spec->flags & SSF_EXT_LAUNCHER)) { + _set_max_num_tasks(step_spec, job_ptr, nodes_avail, +@@ -1464,10 +1467,14 @@ static bitstr_t *_pick_step_nodes(job_record_t *job_ptr, + /* + * An allocating srun will send in the same node_list that was already + * used to construct the job allocation. In that case, we can assume +- * that the job allocation already satifies those requirements. ++ * that the job allocation already satisfies those requirements. An ++ * arbitrary list is always checked: it drives the step's task layout ++ * and indexes the job's resources. + */ +- if (step_spec->node_list && xstrcmp(step_spec->node_list, +- job_ptr->details->req_nodes)) { ++ if (step_spec->node_list && ++ (xstrcmp(step_spec->node_list, job_ptr->details->req_nodes) || ++ ((step_spec->task_dist & SLURM_DIST_STATE_BASE) == ++ SLURM_DIST_ARBITRARY))) { + bitstr_t *selected_nodes = NULL; + log_flag(STEPS, "%s: selected nodelist is %s", + __func__, step_spec->node_list); +@@ -1507,10 +1514,6 @@ static bitstr_t *_pick_step_nodes(job_record_t *job_ptr, + FREE_NULL_BITMAP(selected_nodes); + goto cleanup; + } +- if ((step_spec->task_dist & SLURM_DIST_STATE_BASE) == +- SLURM_DIST_ARBITRARY) { +- step_spec->min_nodes = bit_set_count(selected_nodes); +- } + if (selected_nodes) { + int node_cnt = 0; + /* +@@ -1534,8 +1537,21 @@ static bitstr_t *_pick_step_nodes(job_record_t *job_ptr, + step_spec->max_nodes); + FREE_NULL_BITMAP(selected_nodes); + goto cleanup; +- } else if (step_spec->min_nodes && +- (node_cnt > step_spec->min_nodes)) { ++ } ++ /* ++ * Pin the node count to the list only after the ++ * check above, so that a max_nodes smaller than the ++ * arbitrary list is still rejected rather than ++ * silently overridden. ++ */ ++ if ((step_spec->task_dist & SLURM_DIST_STATE_BASE) == ++ SLURM_DIST_ARBITRARY) { ++ step_spec->min_nodes = node_cnt; ++ step_spec->max_nodes = node_cnt; ++ } ++ ++ if (step_spec->min_nodes && ++ (node_cnt > step_spec->min_nodes)) { + nodes_picked = bit_alloc(bit_size(nodes_avail)); + FREE_NULL_BITMAP(nodes_avail); + nodes_avail = selected_nodes; diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65165-2.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65165-2.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65165-2.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65165-2.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,38 @@ +From ae9894fec202426a7ae2a2a73808b6c2db5713f7 Mon Sep 17 00:00:00 2001 +From: Dominik Bartkiewicz +Date: Wed, 2 Sep 2026 11:03:58 -0400 +Subject: [PATCH] Reject hostlist functions in arbitrary step node list + +Reject one containing a hostlist function ('{'). +An arbitrary step node list must be a literal list of node names. + +(cherry picked from commit ae9894fec202426a7ae2a2a73808b6c2db5713f7) +--- + src/stepmgr/stepmgr.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/src/stepmgr/stepmgr.c b/src/stepmgr/stepmgr.c +index 99b6f88442..f80a4dce2f 100644 +--- a/src/stepmgr/stepmgr.c ++++ b/src/stepmgr/stepmgr.c +@@ -1476,6 +1476,20 @@ static bitstr_t *_pick_step_nodes(job_record_t *job_ptr, + ((step_spec->task_dist & SLURM_DIST_STATE_BASE) == + SLURM_DIST_ARBITRARY))) { + bitstr_t *selected_nodes = NULL; ++ ++ /* ++ * An arbitrary list must be a literal list of node names, not ++ * a hostlist function (feature{...}, switch{...}, ...). ++ */ ++ if (((step_spec->task_dist & SLURM_DIST_STATE_BASE) == ++ SLURM_DIST_ARBITRARY) && ++ xstrchr(step_spec->node_list, '{')) { ++ log_flag(STEPS, "%s: invalid node list %s for arbitrary distribution", ++ __func__, step_spec->node_list); ++ *return_code = ESLURM_INVALID_NODE_NAME; ++ goto cleanup; ++ } ++ + log_flag(STEPS, "%s: selected nodelist is %s", + __func__, step_spec->node_list); + error_code = node_name2bitmap(step_spec->node_list, false, diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65165-3.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65165-3.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65165-3.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65165-3.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,36 @@ +From 6b9442f4ea2fe6b949983a2c4f32449a3955a774 Mon Sep 17 00:00:00 2001 +From: Dominik Bartkiewicz +Date: Tue, 1 Sep 2026 00:12:48 +0100 +Subject: [PATCH] Reject arbitrary step layout with inconsistent node count + +(cherry picked from commit 6b9442f4ea2fe6b949983a2c4f32449a3955a774) +--- + src/common/slurm_step_layout.c | 14 +++++++++++++- + 1 file changed, 13 insertions(+), 1 deletion(-) + +diff --git a/src/common/slurm_step_layout.c b/src/common/slurm_step_layout.c +index f381510360..4b1e350918 100644 +--- a/src/common/slurm_step_layout.c ++++ b/src/common/slurm_step_layout.c +@@ -90,8 +90,20 @@ slurm_step_layout_t *slurm_step_layout_create( + arbitrary_nodes = xstrdup(step_layout_req->node_list); + hl = hostlist_create(step_layout_req->node_list); + hostlist_uniq(hl); ++ /* ++ * The list must span exactly the nodes the caller counted; ++ * otherwise node_cnt would not match the layout. ++ */ ++ if (hostlist_count(hl) != step_layout_req->num_hosts) { ++ error("%s: arbitrary node list %s has %d nodes but %u were expected", ++ __func__, step_layout_req->node_list, ++ hostlist_count(hl), step_layout_req->num_hosts); ++ hostlist_destroy(hl); ++ xfree(arbitrary_nodes); ++ slurm_step_layout_destroy(step_layout); ++ return NULL; ++ } + buf = hostlist_ranged_string_xmalloc(hl); +- step_layout_req->num_hosts = hostlist_count(hl); + hostlist_destroy(hl); + step_layout->node_list = buf; + } else { diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65168-1.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65168-1.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65168-1.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65168-1.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,63 @@ +From 3c4deabda9dac3f93a1a947479a55887fc65ea88 Mon Sep 17 00:00:00 2001 +From: Tim McMullan +Date: Tue, 11 Aug 2026 12:29:48 -0400 +Subject: [PATCH] slurmstepd - Guard against invalid lengths in + getpw/getgr/gethost + +(cherry picked from commit 3c4deabda9dac3f93a1a947479a55887fc65ea88) +--- + src/slurmd/slurmstepd/req.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/src/slurmd/slurmstepd/req.c b/src/slurmd/slurmstepd/req.c +index 68ac76e972..e00cb5d213 100644 +--- a/src/slurmd/slurmstepd/req.c ++++ b/src/slurmd/slurmstepd/req.c +@@ -85,6 +85,13 @@ + #include "src/stepmgr/srun_comm.h" + #include "src/stepmgr/stepmgr.h" + ++/* ++ * Upper bound on the string lengths accepted on the step socket. Every sender ++ * derives these from strlen() of a name or a short message, so this is far ++ * more than any legitimate request needs. ++ */ ++#define MAX_STEPD_REQ_STR_LEN (64 * 1024) ++ + static void *_handle_accept(void *arg); + static int _handle_request(int fd, stepd_step_rec_t *step, + uid_t uid, pid_t remote_pid); +@@ -1688,6 +1695,10 @@ static int _handle_getpw(int fd, stepd_step_rec_t *step, pid_t remote_pid) + safe_read(fd, &mode, sizeof(int)); + safe_read(fd, &uid, sizeof(uid_t)); + safe_read(fd, &len, sizeof(int)); ++ if ((len < 0) || (len > MAX_STEPD_REQ_STR_LEN)) { ++ error("%s: rejecting invalid name length %d", __func__, len); ++ goto rwfail; ++ } + if (len) { + name = xmalloc(len + 1); /* add room for NUL */ + safe_read(fd, name, len); +@@ -1787,6 +1798,10 @@ static int _handle_getgr(int fd, stepd_step_rec_t *step, pid_t remote_pid) + safe_read(fd, &mode, sizeof(int)); + safe_read(fd, &gid, sizeof(gid_t)); + safe_read(fd, &len, sizeof(int)); ++ if ((len < 0) || (len > MAX_STEPD_REQ_STR_LEN)) { ++ error("%s: rejecting invalid name length %d", __func__, len); ++ goto rwfail; ++ } + if (len) { + name = xmalloc(len + 1); /* add room for NUL */ + safe_read(fd, name, len); +@@ -1851,6 +1866,11 @@ static int _handle_gethost(int fd, stepd_step_rec_t *step, pid_t remote_pid) + + safe_read(fd, &mode, sizeof(int)); + safe_read(fd, &len, sizeof(int)); ++ if ((len < 0) || (len > MAX_STEPD_REQ_STR_LEN)) { ++ error("%s: rejecting invalid nodename length %d", ++ __func__, len); ++ goto rwfail; ++ } + if (len) { + nodename = xmalloc(len + 1); /* add room for NULL */ + safe_read(fd, nodename, len); diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65168-2.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65168-2.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65168-2.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65168-2.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,38 @@ +From a6cba9c22ca3721ac42e7086b6c8fd8e508ef979 Mon Sep 17 00:00:00 2001 +From: Tim McMullan +Date: Tue, 11 Aug 2026 12:30:12 -0400 +Subject: [PATCH] slurmstepd - Guard against invalid lengths in the job owner + handlers + +(cherry picked from commit a6cba9c22ca3721ac42e7086b6c8fd8e508ef979) +--- + src/slurmd/slurmstepd/req.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/src/slurmd/slurmstepd/req.c b/src/slurmd/slurmstepd/req.c +index e00cb5d213..7552506749 100644 +--- a/src/slurmd/slurmstepd/req.c ++++ b/src/slurmd/slurmstepd/req.c +@@ -1054,6 +1054,11 @@ _handle_signal_container(int fd, stepd_step_rec_t *step, uid_t uid) + safe_read(fd, &sig, sizeof(int)); + safe_read(fd, &flag, sizeof(int)); + safe_read(fd, &details_len, sizeof(int)); ++ if ((details_len < 0) || (details_len > MAX_STEPD_REQ_STR_LEN)) { ++ error("%s: rejecting invalid details length %d", ++ __func__, details_len); ++ goto rwfail; ++ } + if (details_len) + details = xmalloc(details_len + 1); + safe_read(fd, details, details_len); +@@ -1255,6 +1260,10 @@ _handle_notify_job(int fd, stepd_step_rec_t *step, uid_t uid) + debug3("_handle_notify_job for %ps", &step->step_id); + + safe_read(fd, &len, sizeof(int)); ++ if ((len < 0) || (len > MAX_STEPD_REQ_STR_LEN)) { ++ error("%s: rejecting invalid message length %d", __func__, len); ++ goto rwfail; ++ } + if (len) { + message = xmalloc(len + 1); + safe_read(fd, message, len); diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65168-3.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65168-3.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65168-3.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65168-3.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,37 @@ +From b882d822c117ec538f813f83a2b4ba2dad3987c4 Mon Sep 17 00:00:00 2001 +From: Tim McMullan +Date: Tue, 18 Aug 2026 13:29:20 -0400 +Subject: [PATCH] slurmstepd - Guard against invalid lengths in completion and + reconfig + +(cherry picked from commit b882d822c117ec538f813f83a2b4ba2dad3987c4) +--- + src/slurmd/slurmstepd/req.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/src/slurmd/slurmstepd/req.c b/src/slurmd/slurmstepd/req.c +index 7552506749..579d083cb5 100644 +--- a/src/slurmd/slurmstepd/req.c ++++ b/src/slurmd/slurmstepd/req.c +@@ -2175,6 +2175,10 @@ _handle_completion(int fd, stepd_step_rec_t *step, uid_t uid) + * slurmd and slurmstepd + */ + safe_read(fd, &len, sizeof(int)); ++ if ((len < 0) || (len > MAX_MSG_SIZE)) { ++ error("%s: rejecting invalid jobacct length %d", __func__, len); ++ goto rwfail; ++ } + buf = xmalloc(len); + safe_read(fd, buf, len); + buffer = create_buf(buf, len); +@@ -2462,6 +2466,10 @@ _handle_reconfig(int fd, stepd_step_rec_t *step, uid_t uid) + * len = 0 indicates we're just going for a log rotate. + */ + safe_read(fd, &len, sizeof(int)); ++ if ((len < 0) || (len > MAX_MSG_SIZE)) { ++ error("%s: rejecting invalid config length %d", __func__, len); ++ goto rwfail; ++ } + if (len) { + buffer = init_buf(len); + safe_read(fd, buffer->head, len); diff -Nru slurm-wlm-24.11.5/debian/patches/CVE-2026-65168-4.patch slurm-wlm-24.11.5/debian/patches/CVE-2026-65168-4.patch --- slurm-wlm-24.11.5/debian/patches/CVE-2026-65168-4.patch 1970-01-01 00:00:00.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/CVE-2026-65168-4.patch 2026-09-07 07:28:59.000000000 +0000 @@ -0,0 +1,28 @@ +From b27f8949b7c5d6caf9e59ae79b176a049d2f22af Mon Sep 17 00:00:00 2001 +From: Tim McMullan +Date: Tue, 18 Aug 2026 13:29:36 -0400 +Subject: [PATCH] slurmstepd - Guard against invalid lengths in the attach + handler + +(cherry picked from commit b27f8949b7c5d6caf9e59ae79b176a049d2f22af) + +Reviewed-by: Aron Xu +--- + src/slurmd/slurmstepd/req.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/slurmd/slurmstepd/req.c b/src/slurmd/slurmstepd/req.c +index 579d083cb5..7f5ca19344 100644 +--- a/src/slurmd/slurmstepd/req.c ++++ b/src/slurmd/slurmstepd/req.c +@@ -1384,6 +1384,10 @@ _handle_attach(int fd, stepd_step_rec_t *step, uid_t uid) + safe_read(fd, &srun->ioaddr, sizeof(slurm_addr_t)); + safe_read(fd, &srun->resp_addr, sizeof(slurm_addr_t)); + safe_read(fd, &key_len, sizeof(uint32_t)); ++ if (key_len > MAX_STEPD_REQ_STR_LEN) { ++ error("%s: rejecting invalid key length %u", __func__, key_len); ++ goto rwfail; ++ } + srun->key = xmalloc(key_len); + safe_read(fd, srun->key, key_len); + safe_read(fd, &srun->uid, sizeof(uid_t)); diff -Nru slurm-wlm-24.11.5/debian/patches/series slurm-wlm-24.11.5/debian/patches/series --- slurm-wlm-24.11.5/debian/patches/series 2025-07-13 16:03:19.000000000 +0000 +++ slurm-wlm-24.11.5/debian/patches/series 2026-09-07 07:28:59.000000000 +0000 @@ -8,3 +8,29 @@ force-nvml fix-typos fix-manpages +CVE-2026-65107-1.patch +CVE-2026-65107-2.patch +CVE-2026-65107-3.patch +CVE-2026-65108.patch +CVE-2026-65109-1.patch +CVE-2026-65109-2.patch +CVE-2026-65138-1.patch +CVE-2026-65138-2.patch +CVE-2026-65139-1.patch +CVE-2026-65139-2.patch +CVE-2026-65139-3.patch +CVE-2026-65139-4.patch +CVE-2026-65139-5.patch +CVE-2026-65140-1.patch +CVE-2026-65140-2.patch +CVE-2026-65140-3.patch +CVE-2026-65140-4.patch +CVE-2026-65140-5.patch +CVE-2026-65140-6.patch +CVE-2026-65165-1.patch +CVE-2026-65165-2.patch +CVE-2026-65165-3.patch +CVE-2026-65168-1.patch +CVE-2026-65168-2.patch +CVE-2026-65168-3.patch +CVE-2026-65168-4.patch