DF-2913 / cpulimit_kill.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | /* * DF-2913 — RLIMIT_CPU soft-limit instant SIGKILL when hard limit is * RLIM_INFINITY (rlim_max + 5 signed overflow in plimit_testcpulimit, * sys/kern/kern_plimit.c:467). * * Fork a child that: * 1. installs a SIGXCPU handler that records delivery, * 2. setrlimit(RLIMIT_CPU, {cur = SECS, max = RLIM_INFINITY}) * (unprivileged-legal: cur <= existing max, max unchanged), * 3. busy-spins; if SIGXCPU is received it keeps running for a few * more seconds (proving survival) then exits cleanly. * * Parent reports which signal killed the child / exit status + elapsed. * * BUG expectation (max=RLIM_INFINITY, the DEFAULT hard limit): * child dies with SIGKILL at ~SECS cpu-seconds; * "SIGXCPU received" is NEVER printed; * kernel log shows killproc message "exceeded maximum CPU limit". * CORRECT behavior (control, max=finite): * "SIGXCPU received" printed at ~SECS; child still alive ~3s * later; exits 0 at SECS+3. * * Usage: cpulimit_kill [secs] [infinity|finite] */ #include <sys/resource.h> #include <sys/wait.h> #include <sys/time.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> static volatile sig_atomic_t got_xcpu; static void xcpu_handler(int sig) { got_xcpu = 1; } static double now(void) { struct timeval tv; gettimeofday(&tv, NULL); return (tv.tv_sec + tv.tv_usec / 1e6); } int main(int argc, char **argv) { struct rlimit rl; double t_start, t_end; long secs = (argc > 1) ? atol(argv[1]) : 2; int infinite = (argc < 3 || strcmp(argv[2], "infinity") == 0); pid_t pid, wpid; int st; double t_xcpu = 0; t_start = now(); pid = fork(); if (pid == 0) { struct sigaction sa; double t0; memset(&sa, 0, sizeof(sa)); sa.sa_handler = xcpu_handler; sigaction(SIGXCPU, &sa, NULL); rl.rlim_cur = (rlim_t)secs; rl.rlim_max = infinite ? RLIM_INFINITY : (rlim_t)(secs + 60); if (setrlimit(RLIMIT_CPU, &rl) < 0) { perror("setrlimit"); _exit(9); } printf("child: limits set cur=%ld max=%s (0x%016llx)\n", secs, infinite ? "RLIM_INFINITY" : "finite", (unsigned long long)rl.rlim_max); fflush(stdout); t0 = now(); for (;;) { if (got_xcpu && t_xcpu == 0) { t_xcpu = now(); printf("child: SIGXCPU received at +%.1fs, " "still running\n", t_xcpu - t0); fflush(stdout); } /* survived 3s past the soft limit -> success exit */ if (got_xcpu && now() - t_xcpu > 3.0) _exit(0); } _exit(8); } wpid = waitpid(pid, &st, 0); t_end = now(); printf("mode : rlim_cur=%ld rlim_max=%s\n", secs, infinite ? "RLIM_INFINITY" : "finite(cur+60)"); printf("elapsed wall : %.1f s\n", t_end - t_start); if (wpid < 0) { perror("waitpid"); return (1); } if (WIFSIGNALED(st)) printf("RESULT : child killed by signal %d (%s)\n", WTERMSIG(st), WTERMSIG(st) == SIGKILL ? "SIGKILL" : WTERMSIG(st) == SIGXCPU ? "SIGXCPU" : "?"); else printf("RESULT : child exited status %d " "(survived soft limit)\n", WEXITSTATUS(st)); return (0); } |