fix(apiserver): carry exec_user on job submit so jobs don't run as nobody (bug-1500)

modelJobToSubmit built the C job_startinfo_t without setting execuser, so the
controller forwarded a NULL execuser in the startjob request and the worker
ran every job as "nobody". Now the submit carries the resolved exec user: an
explicit exec_user wins, otherwise the job runs as the submitting user.

- add resolveExecUser() (plain-Go, unit-testable) + TestResolveExecUser
- set si.execuser in modelJobToSubmit, free it in freeJobSubmit

Note: deploying this exposed an ASAN heap-use-after-free in the apiserver's
submit/notification path (strncpy reading freed memory); the fix is committed
but needs the memory-lifecycle investigation before it can ship safely.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
lei.chen 2026-08-04 07:37:48 +00:00
parent ee7dee0ed4
commit 5b47ee603f
8 changed files with 1138 additions and 379 deletions

View file

@ -865,3 +865,27 @@ func TestRawClusterToModel(t *testing.T) {
})
}
}
// bug-1500: the submit conversion must carry an exec user; explicit exec_user
// wins, otherwise the job runs as the submitting user (not "nobody").
func TestResolveExecUser(t *testing.T) {
cases := []struct {
name string
execUser string
submitUser string
want string
}{
{"explicit exec_user wins", "lchen", "admin", "lchen"},
{"empty exec_user falls back to submit user", "", "admin", "admin"},
{"both empty stays empty", "", "", ""},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
job := &models.JobInfo{SubmitUser: tc.submitUser, ExecUser: tc.execUser}
if got := resolveExecUser(job); got != tc.want {
t.Errorf("resolveExecUser: got %q, want %q", got, tc.want)
}
})
}
}