DF-0916 / trigger_fixed.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 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | /* * DF-0916 - PoC trigger / defect demonstrator (FIXED variant). * * Same as trigger.c but with the proposed 2-line clamp fix applied to the * local copy of smb_time_unix2dos. Used to prove the fix converts the * livelock into O(1) termination for the same malicious inputs. * * Build: cc -O2 -o trigger_fixed trigger_fixed.c -lm * Run : ./trigger_fixed [seconds_value] */ #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <limits.h> #include <time.h> #include <signal.h> #include <unistd.h> #include <string.h> #include <sys/time.h> typedef unsigned long u_long; typedef unsigned short u_short; typedef unsigned int u_int; typedef uint16_t u_int16_t; typedef uint8_t u_int8_t; #define DT_2SECONDS_SHIFT 0 #define DT_MINUTES_SHIFT 5 #define DT_HOURS_SHIFT 11 #define DD_DAY_SHIFT 0 #define DD_MONTH_SHIFT 5 #define DD_YEAR_SHIFT 9 static u_short regyear[] = { 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; static u_short leapyear[] = { 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }; /* Iteration cap. Override with $LOOP_CAP env. */ static unsigned long long LOOP_CAP = 500000000ULL; /* 5e8 - ~1s on a fast CPU */ static volatile int timed_out = 0; static void on_alarm(int s) { (void)s; timed_out = 1; } /* * EXACT copy of smb_time_unix2dos year-loop region (sys/vfs/smbfs/smbfs_subr.c * lines 157..194). We instrument the loop with an iteration counter and honor * LOOP_CAP / SIGALRM so the harness cannot livelock the test machine. */ static int smb_time_unix2dos_instrumented(long long tv_sec, int tzoff, u_int16_t *ddp, u_int16_t *dtp, unsigned long long *iters) { u_long t, days, year, inc; u_short *months; u_short lastdtime, lastddate; u_long lastday; unsigned long long n = 0; /* smb_time_local2server: */ t = (u_long)(tv_sec - (long long)tzoff * 60); t &= ~1; /* DF-0916 FIX: clamp t to range representable in a DOS date (year <= 2107) */ if (t > 4323456000UL) t = 4323456000UL; lastdtime = (u_short)((((t / 2) % 30) << DT_2SECONDS_SHIFT) + (((t / 60) % 60) << DT_MINUTES_SHIFT) + (((t / 3600) % 24) << DT_HOURS_SHIFT)); days = t / (24 * 60 * 60); lastday = days; /* The buggy loop. */ for (year = 1970;; year++) { inc = year & 0x03 ? 365 : 366; if (days < inc) break; days -= inc; if (++n >= LOOP_CAP) { *iters = n; return -1; /* hit cap => loop unbounded for this input */ } if (timed_out) { *iters = n; return -2; } } months = year & 0x03 ? regyear : leapyear; /* (the month subloop is bounded by days, so safe.) */ u_long month; for (month = 0; days >= months[month]; month++) ; if (month > 0) days -= months[month - 1]; lastddate = (u_short)(((days + 1) << DD_DAY_SHIFT) + ((month + 1) << DD_MONTH_SHIFT)); if (year > 1980) lastddate += (u_short)((year - 1980) << DD_YEAR_SHIFT); *ddp = lastddate; *dtp = lastdtime; *iters = n; return 0; } int main(int argc, char **argv) { long long input; if (argc >= 2) { if (strcmp(argv[2 % argc], "-") == 0 || strstr(argv[1],"-1")) { /* allow "-1" string */ input = -1LL; } else { input = strtoll(argv[1], NULL, 0); } } else { input = (long long)INT64_MAX; } const char *cap_env = getenv("LOOP_CAP"); if (cap_env) LOOP_CAP = strtoull(cap_env, NULL, 0); printf("DF-0916 smb_time_unix2dos livelock demonstrator\n"); printf(" input tv_sec = %lld (reinterpreted as u_long = 0x%016llx, days=%llu)\n", input, (unsigned long long)(u_long)input, (unsigned long long)((u_long)input / (24UL*60*60))); printf(" LOOP_CAP = %llu iterations\n", LOOP_CAP); printf(" iter cap simulates a hard watchdog (real kernel code has NONE)\n"); u_int16_t ddp = 0, dtp = 0; unsigned long long iters = 0; timed_out = 0; struct sigaction sa; memset(&sa, 0, sizeof sa); sa.sa_handler = on_alarm; sigaction(SIGALRM, &sa, NULL); /* Wall-clock safety: 10s alarm so the harness never wedges. */ alarm(10); struct timeval t0, t1; gettimeofday(&t0, NULL); int rc = smb_time_unix2dos_instrumented(input, /*tzoff*/0, &ddp, &dtp, &iters); gettimeofday(&t1, NULL); double dt = (t1.tv_sec - t0.tv_sec) + (t1.tv_usec - t0.tv_usec) / 1e6; if (rc == 0) { printf("RESULT: TERMINATED in %llu iterations, %.6fs; ddp=0x%04x dtp=0x%04x\n", iters, dt, ddp, dtp); return 0; } else if (rc == -1) { printf("RESULT: LIVELOCK - hit LOOP_CAP after %llu iterations (%.2fs)\n", iters, dt); printf(" Kernel code (no cap) would continue past %llu iterations;\n", iters); printf(" full iteration count for this input ~= %llu => minutes of kernel CPU.\n", (unsigned long long)((u_long)input / (24UL*60*60) / 365)); return 2; } else { printf("RESULT: TIMEOUT - 10s wall alarm fired after %llu iterations\n", iters); return 3; } } |