main: add rlimit support via UCS configuration#14
main: add rlimit support via UCS configuration#14namansh70747 wants to merge 1 commit intonubificus:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Adds support for parsing OCI-style RLIMIT: entries from the UCS configuration block and applying them via setrlimit(2) before dropping privileges, enabling urunit-side rlimits support that complements urunc serialization.
Changes:
- Extend
process_configto carry parsed rlimit resources and values. - Parse
RLIMIT:<type>:<soft>:<hard>lines from UCS and allocate rlimit arrays up-front. - Apply parsed rlimits in
setup_exec_env()prior tosetgid/setuid, and update cleanup paths.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| free(app_config->pr_conf->rlimits); | ||
| free(app_config->pr_conf->rlimit_resources); | ||
| free(app_config->pr_conf); |
There was a problem hiding this comment.
child_func_free dereferences app_config->pr_conf unconditionally. get_config_from_file() can legitimately set econf->pr_conf = NULL (e.g., missing/invalid UCS block), which will segfault here. Guard app_config->pr_conf before freeing its fields (and consider freeing it as a unit in a helper).
| free(app_config->pr_conf->rlimits); | |
| free(app_config->pr_conf->rlimit_resources); | |
| free(app_config->pr_conf); | |
| if (app_config->pr_conf) { | |
| free(app_config->pr_conf->rlimits); | |
| free(app_config->pr_conf->rlimit_resources); | |
| free(app_config->pr_conf); | |
| } |
| while (remaining > 7 && *scan != '\0') { | ||
| if (memcmp(scan, "RLIMIT:", 7) == 0) | ||
| total_rlimits++; | ||
| // Advance to next line | ||
| while (remaining > 0 && *scan != '\n' && *scan != '\0') { | ||
| scan++; | ||
| remaining--; | ||
| } | ||
| if (remaining > 0 && *scan == '\n') { | ||
| scan++; | ||
| remaining--; | ||
| } | ||
| } |
There was a problem hiding this comment.
The RLIMIT pre-scan loop uses while (remaining > 7 && *scan != '\0'), which skips checking a line when exactly 7 bytes remain. If the input ends with a malformed/truncated RLIMIT: line, total_rlimits may stay 0 and later RLIMIT parsing can write via NULL conf->rlimits. Use remaining >= 7 and/or make RLIMIT parsing robust to total_rlimits == 0.
| } else if (memcmp(tmp_field, "RLIMIT:", 7) == 0) { | ||
| int resource = 0; | ||
| rlim_t soft = 0, hard = 0; | ||
| ret = get_rlimit_vals(tmp_field, &resource, &soft, &hard); | ||
| if (ret != 0) { | ||
| fprintf(stderr, "Failed to parse RLIMIT line: %s\n", tmp_field); | ||
| break; | ||
| } | ||
| size_t n = conf->rlimits_count; | ||
| conf->rlimits[n].rlim_cur = soft; | ||
| conf->rlimits[n].rlim_max = hard; | ||
| conf->rlimit_resources[n] = resource; | ||
| conf->rlimits_count++; |
There was a problem hiding this comment.
When handling RLIMIT: entries, the code writes to conf->rlimits[n] / conf->rlimit_resources[n] without checking that the arrays were allocated and that n is within the allocated capacity. Even if the current pre-scan is expected to over-count, this is still an input-parsing edge case that can become a memory safety bug if the counting logic ever diverges. Track the allocated capacity and validate before writing (fail parsing if exceeded).
| DEBUG_PRINTF("Setting rlimit resource %d soft=%lu hard=%lu\n", | ||
| process_conf->rlimit_resources[i], | ||
| (unsigned long)process_conf->rlimits[i].rlim_cur, | ||
| (unsigned long)process_conf->rlimits[i].rlim_max); |
There was a problem hiding this comment.
DEBUG_PRINTF("... soft=%lu hard=%lu") casts rlim_t to unsigned long. On platforms where rlim_t is wider than unsigned long (or on 32-bit builds), this can truncate values and mislead debugging. Prefer printing via uintmax_t/%ju (cast) or use an appropriate format macro for rlim_t.
| DEBUG_PRINTF("Setting rlimit resource %d soft=%lu hard=%lu\n", | |
| process_conf->rlimit_resources[i], | |
| (unsigned long)process_conf->rlimits[i].rlim_cur, | |
| (unsigned long)process_conf->rlimits[i].rlim_max); | |
| DEBUG_PRINTF("Setting rlimit resource %d soft=%ju hard=%ju\n", | |
| process_conf->rlimit_resources[i], | |
| (uintmax_t)process_conf->rlimits[i].rlim_cur, | |
| (uintmax_t)process_conf->rlimits[i].rlim_max); |
| if (app_config) { | ||
| free(app_config->envs); | ||
| free(app_config->pr_conf->rlimits); | ||
| free(app_config->pr_conf->rlimit_resources); | ||
| free(app_config->pr_conf); | ||
| free(app_config); | ||
| } |
There was a problem hiding this comment.
The new cleanup frees envs and pr_conf but still doesn't free blk_conf (and the per-entry struct block_config allocations from parse_block_config) on the failure paths where manual_execvpe returns. Since this block is being updated, consider completing the cleanup for blk_conf too to avoid leaks during error handling.
| ret = setrlimit(process_conf->rlimit_resources[i], | ||
| &process_conf->rlimits[i]); | ||
| if (ret < 0) { | ||
| perror("set rlimit"); |
There was a problem hiding this comment.
perror("set rlimit") doesn’t match the syscall name (setrlimit) and loses context about which resource failed. Consider using a clearer message (e.g., include the resource id / original type string) so failures are diagnosable from logs.
| perror("set rlimit"); | |
| char err_msg[64]; | |
| snprintf(err_msg, sizeof(err_msg), | |
| "setrlimit(resource %d)", | |
| process_conf->rlimit_resources[i]); | |
| perror(err_msg); |
bdda119 to
6e16f27
Compare
Parse RLIMIT:TYPE:SOFT:HARD entries from the UCS block and apply them via setrlimit(2) before privilege drop in setup_exec_env(). The type string (e.g. RLIMIT_NOFILE) is resolved to a POSIX resource integer using a static lookup table. Limits are stored in two parallel dynamically allocated arrays inside struct process_config: rlimits[] for the struct rlimit values and rlimit_resources[] for the resource integers. Both are freed on cleanup. This is the urunit side of the OCI rlimits feature. The urunc side that serializes these entries is in urunc-dev/urunc#558. Signed-off-by: namansh70747 <namansh70747@gmail.com>
6e16f27 to
886c58d
Compare
Add support for reading and applying resource limits (rlimits) to the
target application as specified in the UCS configuration block.
The UCS block is pre-scanned to count
RLIMIT:entries so both arraysare allocated exactly once. The type string (e.g.
RLIMIT_NOFILE) isresolved to a POSIX resource integer via
get_rlimit_vals(). Limits areapplied via
setrlimit(2)before privilege drop insetup_exec_env()and freed on all cleanup paths.
The configuration format is:
This is the urunit side of OCI rlimits support. The urunc side that
serializes these entries into
URUNIT_CONFIGis in urunc-dev/urunc#558.