DF-4865 / cpuctl_probe.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 | /* * DF-4865 pass-2 live probe for sys/dev/misc/cpuctl/cpuctl.c * * Documents, on the running guest: * 1. /dev/cpuctl0 permission gate (unprivileged user cannot open at all) * 2. CPUCTL_RDMSR on a valid MSR (MSR_BIOS_SIGN 0x8B) succeeds * 3. CPUCTL_RDMSR / CPUCTL_WRMSR / CPUCTL_MSRSBIT / CPUCTL_MSRCBIT on an * architecturally-invalid MSR return EFAULT and DO NOT panic * (rdmsr_safe/wrmsr_safe + msr_onfault path -- the DF-4735/DF-4825 * #GP family does NOT apply to the cpuctl ioctl path) * 4. CPUCTL_CPUID / CPUCTL_CPUID_COUNT return zeroed, well-formed data * 5. CPUCTL_UPDATE argument validation (NULL/0-size -> EINVAL) and a * garbage-blob update attempt -> the "updated microcode from rev=X to * rev=Y" kprintf carries *initialized* values on this CPU * (MSR_BIOS_SIGN reads fine under QEMU, so the uninit-print branch of * DF-4865 cannot be triggered on this guest -> not_reproduced here) * * cc -O -o /tmp/cpuctl_probe /tmp/cpuctl_probe.c && /tmp/cpuctl_probe */ #include <sys/types.h> #include <sys/ioctl.h> #include <sys/cpuctl.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> static int rdmsr(int fd, int msr, uint64_t *out) { cpuctl_msr_args_t a; int r; memset(&a, 0xa5, sizeof(a)); /* poison: detect stale writes */ a.msr = msr; r = ioctl(fd, CPUCTL_RDMSR, &a); if (r == 0 && out) *out = a.data; return (r); } int main(void) { cpuctl_msr_args_t a; cpuctl_cpuid_args_t cid; cpuctl_cpuid_count_args_t cc; cpuctl_update_args_t up; uint64_t v; int fd, r, i; printf("== 1. unprivileged open gate ==\n"); fd = open("/dev/cpuctl0", O_RDONLY); if (fd < 0) printf("open(O_RDONLY) as this user: %s (errno=%d)\n", strerror(errno), errno); else printf("open(O_RDONLY) OK -- user CAN read\n"); fd = open("/dev/cpuctl0", O_RDWR); if (fd < 0) { printf("open(O_RDWR): %s (errno=%d)\n", strerror(errno), errno); fd = open("/dev/cpuctl0", O_RDONLY); if (fd < 0) { printf("cannot open at all: %s\n", strerror(errno)); return (1); } } printf("\n== 2. RDMSR valid (MSR_BIOS_SIGN 0x8b) ==\n"); r = rdmsr(fd, 0x8b, &v); printf("rdmsr(0x8b) = %d data=0x%016jx errno=%d (%s)\n", r, (uintmax_t)v, errno, r ? strerror(errno) : "ok"); printf("\n== 3. safe-fault family on invalid MSR 0x11111111 ==\n"); r = rdmsr(fd, 0x11111111, &v); printf("rdmsr_safe path: ret=%d errno=%d (%s) data=0x%016jx\n", r, errno, strerror(errno), (uintmax_t)v); memset(&a, 0, sizeof(a)); a.msr = 0x11111111; a.data = 0x4141414141414141ULL; r = ioctl(fd, CPUCTL_WRMSR, &a); printf("wrmsr_safe path: ret=%d errno=%d (%s)\n", r, errno, r ? strerror(errno) : "ok"); memset(&a, 0, sizeof(a)); a.msr = 0x11111111; a.data = 0x1; r = ioctl(fd, CPUCTL_MSRSBIT, &a); printf("msrsbit_safe path: ret=%d errno=%d (%s)\n", r, errno, r ? strerror(errno) : "ok"); memset(&a, 0, sizeof(a)); a.msr = 0x11111111; a.data = 0x1; r = ioctl(fd, CPUCTL_MSRCBIT, &a); printf("msrcbit_safe path: ret=%d errno=%d (%s)\n", r, errno, r ? strerror(errno) : "ok"); printf("guest still alive after safe-fault family\n"); printf("\n== 4. CPUID framing ==\n"); memset(&cid, 0xa5, sizeof(cid)); cid.level = 1; r = ioctl(fd, CPUCTL_CPUID, &cid); printf("cpuid(1): ret=%d eax=0x%08x ebx=0x%08x ecx=0x%08x edx=0x%08x\n", r, cid.data[0], cid.data[1], cid.data[2], cid.data[3]); memset(&cid, 0xa5, sizeof(cid)); cid.level = 0x7ffffff9; /* invalid level */ r = ioctl(fd, CPUCTL_CPUID, &cid); printf("cpuid(0x7ffffff9): ret=%d data=%08x/%08x/%08x/%08x (no stale 0xa5a5a5a5)\n", r, cid.data[0], cid.data[1], cid.data[2], cid.data[3]); memset(&cc, 0xa5, sizeof(cc)); cc.level = 4; /* deterministic leaf: needs subleaf */ cc.level_type = 0; r = ioctl(fd, CPUCTL_CPUID_COUNT, &cc); printf("cpuid_count(4,0): ret=%d data=%08x/%08x/%08x/%08x\n", r, cc.data[0], cc.data[1], cc.data[2], cc.data[3]); printf("\n== 5. UPDATE arg validation + garbage blob (root only) ==\n"); memset(&up, 0, sizeof(up)); up.data = NULL; up.size = 0; r = ioctl(fd, CPUCTL_UPDATE, &up); printf("update(NULL,0): ret=%d errno=%d (%s)\n", r, errno, r ? strerror(errno) : "ok"); up.data = (void *)1; /* unmapped -> copyin fault */ up.size = 64; r = ioctl(fd, CPUCTL_UPDATE, &up); printf("update(bad ptr,64): ret=%d errno=%d (%s)\n", r, errno, r ? strerror(errno) : "ok"); { unsigned char blob[64]; memset(blob, 0x5a, sizeof(blob)); up.data = blob; up.size = sizeof(blob); r = ioctl(fd, CPUCTL_UPDATE, &up); printf("update(garbage,64): ret=%d errno=%d (%s)\n", r, errno, r ? strerror(errno) : "ok"); } printf("done\n"); return (0); } |