fix(harness): xce_test argument validation, RPC error text and leaks (13 bugs)
Stack overflows on user input:
- bug-1702 jobrun -m: host segment strncpy'd into a 256-byte buffer
unbounded (ASAN stack-buffer-overflow); now cmdutil_split_host_tasks()
refuses an over-long host.
- bug-1703 jobsub -n: min strncpy'd into a 64-byte buffer; now
cmdutil_parse_ntasks() parses in place, 1 <= min <= max.
- resupdate's 16-entry vals[] had no bound either; a 17th -n overflowed.
Wrong or silent results:
- bug-1574 resupdate type/model: set_resval() had no string case, left the
entry's name uninitialized, and crashed the encoder. Moved to
resval_typeresolve.c as resval_fill(), which fills every type.
- bug-1705 jobsub -x was overwritten (and leaked) by the default exdata;
cmdutil_default_exdata() only fills an unset one. jobsub's
execuser/exechome/execcwd copies leaked on every call.
- bug-1707 a trailing -n without -v was dropped silently; resupdate also
returned 0 after its RPC failed and "succeeded" with nothing to send.
- bug-1708 jobkill -s SIGKILL became signal 0 and was sent.
- bug-1709 jobinfo exited 0 with no -j, a malformed -j, or no hits.
- bug-1710 hello -c 0 sent nothing and reported success.
- bug-1712 a duplicate -v was reported as "no res name found".
- bug-1777 workerlist showed join-time maxmem/maxswap/maxtmp, not the
effective values (hosts.yaml overrides) the reply already carries in
resvals_to_sched and the scheduler uses.
- bug-1793 join's maxswap/maxtmp defaults were 1 KB / 2 KB in bytes; now
1 GiB / 2 GiB, and -m/-s/-t no longer overflow atoi() above 2 GB.
Error reporting and leaks:
- bug-1796 RPC failures printed perror() of a stale errno ("Connection
refused" / "Invalid argument" / "Inappropriate ioctl for device" for
the same failure). cmdutil_rpc_error() prints the RPC's own code
(E_XCE_* included) at all 19 sites.
- bug-1797 qctrl leaked the queue name on the RPC-error path; repeated
-q/-h leaked the earlier copy.
New src/harness/cmdutil.c (+ cmdutil_unit_test, 14 cases); 9 new
test_test_cli.c cases, 8 RED on the old xce_test; new resval_fill,
worker_effective_attr and join-default tests (join ones RED on the old
join_worker_args.c). Harness suite 6/6 (asan), harness_unit_test 141/0.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
eee76a75ab
commit
da1a663f05
14 changed files with 1107 additions and 286 deletions
41
src/harness/include/cmdutil.h
Normal file
41
src/harness/include/cmdutil.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* cmdutil.h -- xce_test command-argument and RPC-error helpers, extracted
|
||||
* from test.c so they can be unit-tested without a live controller
|
||||
* (testsuite/test_cmdutil.c).
|
||||
*/
|
||||
#ifndef CMDUTIL_H
|
||||
#define CMDUTIL_H
|
||||
|
||||
#include <stddef.h> /* size_t */
|
||||
#include <stdint.h> /* uint32_t */
|
||||
|
||||
/* Parse all of `s` as a base-10 integer in [lo, hi] into *out.
|
||||
* Returns 0, -EINVAL (empty, NULL or not entirely numeric) or -ERANGE
|
||||
* (outside [lo, hi]); *out is left unchanged on error. */
|
||||
int cmdutil_parse_long(const char* s, long lo, long hi, long* out);
|
||||
|
||||
/* Split jobrun's "<host>[:<ntasks>]" into `host` (hostsz bytes including the
|
||||
* NUL) and *ntasks (1 when omitted). Returns 0, -ENAMETOOLONG (host segment
|
||||
* does not fit), or -EINVAL (empty host, or ntasks not an integer >= 1). */
|
||||
int cmdutil_split_host_tasks(const char* arg, char* host, size_t hostsz,
|
||||
int* ntasks);
|
||||
|
||||
/* Parse jobsub's "<min>[,<max>]" (max defaults to min). Returns 0, -EINVAL
|
||||
* (not numeric, or max < min) or -ERANGE (min < 1, or beyond UINT32_MAX). */
|
||||
int cmdutil_parse_ntasks(const char* arg, uint32_t* min, uint32_t* max);
|
||||
|
||||
/* jobsub's default exdata: when *exdata is NULL, set it to a newly
|
||||
* allocated {"sub_env_vars":{"PATH":<path>}} JSON string; an exdata the
|
||||
* caller already set (-x) is left alone. Returns 0 or -ENOMEM. */
|
||||
int cmdutil_default_exdata(char** exdata, const char* path);
|
||||
|
||||
/* Text for an RPC's negative return code `rc` (libc errno or E_XCE_*), in
|
||||
* `buf`; never reads the global errno. Returns buf. */
|
||||
const char* cmdutil_errstr(int rc, char* buf, size_t bufsize);
|
||||
|
||||
/* Print "<what>: <cmdutil_errstr(rc)>" to stderr and set errno to -rc: the
|
||||
* replacement for perror() on an RPC-failure path, where errno does not
|
||||
* hold the RPC's result. */
|
||||
void cmdutil_rpc_error(const char* what, int rc);
|
||||
|
||||
#endif /* CMDUTIL_H */
|
||||
|
|
@ -23,6 +23,9 @@
|
|||
#include <stdint.h> /* uint32_t */
|
||||
#include <time.h> /* time_t */
|
||||
|
||||
#include "controller.capnp.h" /* Value_u_which */
|
||||
#include "xce/types.h" /* xce_worker_t */
|
||||
|
||||
/* Render a worker's status bitmask (xce_worker_t.status, XCE_WORKER_* bits
|
||||
* in xce/types.h) as a short display string for cmd_workerlist's Status
|
||||
* column.
|
||||
|
|
@ -78,4 +81,12 @@ char* job_state_str(int state);
|
|||
* `buf`. */
|
||||
const char* format_job_time(time_t t, char* buf, size_t bufsize);
|
||||
|
||||
/* The value workerlist shows for a worker's byte-valued host attribute
|
||||
* (`resname` = "maxmem", "maxswap" or "maxtmp"): the effective numeric
|
||||
* value the controller reports in w->resvals_to_sched (it reflects a
|
||||
* hosts.yaml override, and is what the scheduler uses), else
|
||||
* `attr_value`, the join-time attrs field (bug-1777). */
|
||||
double worker_effective_attr(const xce_worker_t* w, const char* resname,
|
||||
double attr_value);
|
||||
|
||||
#endif /* JOBLIST_DISPLAY_H */
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
|
||||
#include "controller.capnp.h" /* enum Value_u_which */
|
||||
#include "xce/types.h" /* xce_resval_t */
|
||||
|
||||
/* Sentinel: not a valid Value_u_which value. Used both as:
|
||||
* - the RETURN value when `name` is not present in the known-resource
|
||||
|
|
@ -50,4 +51,16 @@
|
|||
*/
|
||||
int resval_resolve_type(const char* name, int type_override);
|
||||
|
||||
/* Fill `rv` for one resupdate entry: rv->name = name and rv->host = host
|
||||
* (both borrowed, not copied), and a newly allocated rv->val of the type
|
||||
* resval_resolve_type() gives, holding `val` (NULL = the type's zero value;
|
||||
* a string value is copied). Formerly test.c's set_resval(), which had no
|
||||
* string case: a type/model entry kept an uninitialized name (bug-1574).
|
||||
* Returns 1, 0 when `name` is NULL (nothing filled), or -ENOMEM. */
|
||||
int resval_fill(xce_resval_t* rv, char* name, const char* val,
|
||||
int type_override, char* host);
|
||||
|
||||
/* Free what resval_fill() allocated (rv->val and a string value in it). */
|
||||
void resval_fill_release(xce_resval_t* rv);
|
||||
|
||||
#endif /* RESVAL_TYPERESOLVE_H */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue