DF-2737 / prober.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 166 | /* * DF pass-2 kern_sysctl.c adversarial prober (unprivileged). * Exercises: name2oid edges, exact-path oidfmt/oiddescr with sprayed stale * stack, oldlenp/oldlen truncation games on multi-SYSCTL_OUT nodes, * write gating. Success criterion: no panic, sane errno/length semantics. */ #include <sys/types.h> #include <sys/sysctl.h> #include <sys/param.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> static int q(const char *name, void *old, size_t *oldlen) { return sysctlbyname(name, old, oldlen, NULL, 0); } static void raw(int *name, u_int namelen, void *old, size_t *oldlen, void *new, size_t newlen) { int r = sysctl(name, namelen, old, oldlen, new, newlen); printf("raw([%d,...],%u) = %d errno=%d (%s) len=%zu\n", name[0], namelen, r, r < 0 ? errno : 0, r < 0 ? strerror(errno) : "ok", oldlen ? *oldlen : 0); } int main(void) { char buf[2048]; size_t len; int r, i; /* ---- 1. name2oid edge cases ({0,3}) ---- */ { int nm[2] = {0, 3}; int oid[CTL_MAXNAME]; size_t oidlen; char big[MAXPATHLEN]; /* 1024 */ /* newlen = 0 */ oidlen = sizeof(oid); r = sysctl(nm, 2, oid, &oidlen, (void *)"", 0); printf("name2oid newlen=0 -> %d errno=%d\n", r, errno); /* newlen = 1, no NUL needed (len==1 exact) */ oidlen = sizeof(oid); r = sysctl(nm, 2, oid, &oidlen, (void *)"k", 1); printf("name2oid newlen=1 -> %d errno=%d\n", r, errno); /* newlen = MAXPATHLEN (rejected: ENAMETOOLONG) */ oidlen = sizeof(oid); memset(big, 'x', sizeof(big)); r = sysctl(nm, 2, oid, &oidlen, big, sizeof(big)); printf("name2oid newlen=1024 -> %d errno=%d (%s)\n", r, errno, strerror(errno)); /* newlen = MAXPATHLEN-1, no NUL anywhere */ oidlen = sizeof(oid); r = sysctl(nm, 2, oid, &oidlen, big, sizeof(big) - 1); printf("name2oid newlen=1023 -> %d errno=%d (%s)\n", r, errno, strerror(errno)); /* all dots */ oidlen = sizeof(oid); r = sysctl(nm, 2, oid, &oidlen, (void *)"....", 4); printf("name2oid dots -> %d errno=%d\n", r, errno); /* valid + trailing junk past 12 components */ oidlen = sizeof(oid); r = sysctl(nm, 2, oid, &oidlen, (void *)"kern.ipc.shm.info.x.y.z.w", 25); printf("name2oid deep -> %d errno=%d\n", r, errno); } /* ---- 2. exact-path oidfmt/oiddescr w/ sprayed stale stack ---- */ for (i = 0; i < 64; i++) { /* seed kernel stack frame of __sysctl with a 12-int name */ int nm[CTL_MAXNAME] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; size_t pl = 0; sysctl(nm, 12, NULL, &pl, NULL, 0); /* ENOENT expected */ /* now {0,4} namelen=2: handler re-reads uninitialized name[2] */ { int fm[2] = {0, 4}; unsigned char ob[64]; size_t ol = sizeof(ob); sysctl(fm, 2, ob, &ol, NULL, 0); } { int ds[2] = {0, 5}; unsigned char ob[256]; size_t ol = sizeof(ob); sysctl(ds, 2, ob, &ol, NULL, 0); } } printf("oidfmt/oiddescr exact-path stale-stack x64: no panic\n"); /* ---- 3. truncation games on multi-OUT nodes ---- */ /* sysctl.name of a real oid = several separate SYSCTL_OUT calls */ { int nm[4] = {0, 1, 1, 99}; /* name of kern.??? unknown */ size_t pl = 0; r = sysctl(nm, 4, NULL, &pl, NULL, 0); printf("sysctl.name query len -> %d errno=%d pl=%zu\n", r, errno, pl); } { int nm[4] = {0, 1, 1, 28}; /* name of kern.bootfile? guess */ char ob[8]; size_t ol; for (ol = 0; ol <= 8; ol++) { size_t l2 = ol; memset(ob, 0xAA, sizeof(ob)); r = sysctl(nm, 4, ob, &l2, NULL, 0); printf("sysctl.name oldlen=%zu -> r=%d errno=%d " "retlen=%zu ob[0..3]=%02x%02x%02x%02x\n", ol, r, errno, l2, ob[0], ob[1], ob[2], ob[3]); } } /* kern.bootfile string with tiny oldlen */ for (len = 0; len <= 6; len++) { size_t l2 = len; memset(buf, 0x5A, 16); r = q("kern.bootfile", buf, &l2); printf("kern.bootfile oldlen=%zu -> r=%d errno=%d retlen=%zu " "buf0=%02x\n", len, r, errno, l2, (unsigned char)buf[0]); } /* opaque struct node: kern.clockrate with oldlen=1 */ { size_t l2 = 1; memset(buf, 0x5A, sizeof(buf)); r = q("kern.clockrate", buf, &l2); printf("kern.clockrate oldlen=1 -> r=%d errno=%d retlen=%zu\n", r, errno, l2); } /* ---- 4. write gating ---- */ { int nv = 1; r = sysctlbyname("debug.sysctl", NULL, NULL, &nv, sizeof(nv)); printf("write debug.sysctl -> %d errno=%d (%s)\n", r, errno, strerror(errno)); r = sysctlbyname("kern.hostname", NULL, NULL, (void *)"x", 1); printf("write kern.hostname -> %d errno=%d (%s)\n", r, errno, strerror(errno)); } /* ---- 5. adversarial next-walk namelen ---- */ { int nm[12] = {0, 2, 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff}; int ob[16]; size_t ol = sizeof(ob); raw(nm, 12, ob, &ol, NULL, 0); nm[2] = -1; nm[3] = -1; ol = sizeof(ob); raw(nm, 12, ob, &ol, NULL, 0); } printf("PROBER-COMPLETE\n"); return 0; } |