Eight concepts of reference, and the reader is left with a set of tables about a machine they are not running on. This concept is the inventory, and it has a property none of the others has: every row in it was produced by this process on this machine, and every row is either a bit pattern read out of a register or a signal number that killed a forked child.
That is worth having on its own terms even if you will never read another word about the control registers. A user program that wants to know something about its own machine has a finite, enumerable list of things it can ask, and the list is shorter than most people assume, longer than most people guess, and the boundary of it is not where the documentation says.
Every fault in this course is a signal, and the parent has to learn the signal's si_code and si_addr from a child. How it learns them is the whole design, and the first version got it wrong in the way that is most expensive to notice.
The artifact prints both halves of that story in section 3, next to the table, because a harness failure is part of the instrument and a course that reports only its final table has hidden the part of the method that a reader most needs to check.
First the can column. Ten instructions, each in its own forked child, each reported by name:
$ ./sysdump | grep '^ EDGEREAD'
EDGEREAD | sidt | si_code 0 | readable
EDGEREAD | sgdt | si_code 0 | readable
EDGEREAD | sldt | si_code 0 | readable
EDGEREAD | str | si_code 0 | readable
EDGEREAD | smsw | si_code 0 | readable
EDGEREAD | rdtsc | si_code 0 | readable
EDGEREAD | rdtscp | si_code 0 | readable
EDGEREAD | xgetbv | si_code 0 | readable
EDGEREAD | rdfsbase | si_code 0 | readable
EDGEREAD | wrfsbase | si_code 0 | readable
And the shape of the cannot column, which is the number and one more thing:
$ ./sysdump | grep '^ EDGE | rows'
EDGE | rows 50 | faulted 39 | returned 11 | si_code 128 34 | other 5
Fifty instructions, thirty-nine faults, eleven returns — and the harness asserts the two sets are disjoint, because a partition is a claim a check can make and a count cannot.
si_code 128 with si_addr 0. A #GP with an empty error code. The five that are not are the #UD group — ud2, sysenter, vmcall, mgmt and a raw bound encoding — and those arrive as SIGILL with an si_addr that is the faulting instruction. Two different signals for the same boundary, and the difference is whether the CPU knew what the instruction meant.sysret and sysenter are on opposite sides of that line.sysret from ring 3 is si_code 128 and sysenter is si_code 2 as a SIGILL. Two instructions whose only similarity is the word SYSCALL, and the privilege course measured the second one and found it was a vendor difference about long mode rather than a privilege rule. A boundary table is worth more than a boundary rule, because a rule would have had to pick one.mov $0x1234,%ax; mov %ax,%ds is a #GP; mov %ds,%ax returns. And str returns while ltr does not. The boundary is not “the machine's tables”; it is per-instruction, and the two halves of a register access are on opposite sides of it.$ ./sysdump | sed -n '/6C\./,/XCR0 = /p'
6C. THERE IS NO AVX-512, WHICH IS A DIFFERENT KIND OF ABSENCE,
because it is a FEATURE BIT rather than a permission.
ABSENT | CPUID.7.0:EBX AVX512F 0 DQ 0 CD 0 BW 0 VL 0 | ALL ZERO
ABSENT | XCR0 = 0x0000000000000207, and the AVX-512 STATE bits of XCR0
ABSENT | (5 opmask, 6 zmm_hi256, 7 hi16_zmm) are all 0, so even
ABSENT | the state is not merely unsupported, it is not enabled.
ABSENT | A feature bit is a FACT ABOUT THE CPU that any process
ABSENT | can read with an unprivileged instruction, which is the
ABSENT | opposite of a permission: the absence of AVX-512 is
ABSENT | DECLARED BY THE HARDWARE, and the absence of the PMU is
ABSENT | imposed by software. Two absences, two different kinds.
That is the concept's whole argument in one paragraph, and it is a distinction rather than a number. An absence of hardware is declared by the hardware, in a register any process may read. An absence of access is imposed by software, in a file and a bit and a file mode. The first is a fact about the CPU and identical on every machine of that model. The second is a fact about this machine, today, as configured, and it can change without the CPU changing — which is why the performance-counter absence needed a fork to establish and the AVX-512 absence did not need a fork at all.
Every one is printed by the artifact and asserted as text by the harness, so a taken-back claim can be neither dropped nor edited into being right. They are worth reading as a list because the pattern of them is the argument for the whole collection:
$ ./sysdump | sed -n '/^ R1\./,/^ R3\./p'
R1. "A ring-3 process cannot read the IDT because the IDT is
privileged." RETRACTED AND REPLACED. The REGISTER is
readable: SIDT returns base and limit from ring 3, and section
2 prints them. What cannot be read is one byte of the TABLE,
and the reason is not privilege: the fault is si_code 1,
SEGV_MAPERR, with si_addr EXACTLY the base SIDT printed. A
#GP would have been si_code 128 and si_addr 0, which is what
every privileged instruction in section 3 produced. The right
sentence is a PAGE-TABLE sentence, and it predicts the address
the first sentence could not.
R2. "The 16-byte arm of the canonical sweep measures the
canonical boundary." RETRACTED. It measured an ALIGNMENT
requirement, because gcc compiled the __int128 load to
`movdqa`, and the sweep then reported si_code 128 for every
unaligned address at every offset, which looks like a rule and
is not one. Written in inline `movdqu` the same sweep gives
2^47 - 15, the value the rule predicts, and section 5 prints
BOTH arms side by side so that the retraction has numbers.
Two of the ten are not about the machine at all, and they are the two that a reader is most likely to repeat.
syscall table reported all fifteen clobbered, because the inline-asm block's clobber list named six registers the body wrote and said nothing about seven more. A SIGBUS from a held output address came first; the wrong table came second; and the wrong table agreed with a story the author had already written down, which is the kind of error that survives review. The general form is in the first concept and the retraction is here because a course that keeps its harness bugs to itself is teaching that harness bugs are not knowledge.EAX bit 26. The first version read subleaf 0 ECX bit 26 — the same number, a different register, a different subleaf — got zero, and reported LAM as unsupported with total confidence. It happened to be right and it was still wrong, and the general form is worse than the error: a check that reads the wrong bit has not been tested at all, and neither has the code around it.And the two that are about reading a document correctly, which is where a reference course lives or dies:
movdqa one. A compiler strengthened an instruction nobody asked it to strengthen, and the measurement acquired a pattern that a reader would take for a machine rule. The two arms are printed side by side in the artifact so that the retraction has numbers rather than an adjective. The general form is in this course's third concept too, and it has now happened here twice: a measurement harness must be read, not trusted, and objdump on the body is the cheapest possible check./proc/self/pagemap word is the present bit; bit 63 of the page-table entry is NX. The first version read the first as the second and reported NX=1 for a page it had never written. Two different bits, the same number, and a page that confuses them is confidently wrong about a security property. The replacement is the better result anyway — an untouched mapping has no entry at all, which is demand paging observed from the inside.cd courses/x86sys/assets/samples && ./build_samples.sh, then read section 3. (Expect 50 rows, 39 faults, 11 returns, and the two sets disjoint. If a row flips, the most likely causes in order are a seccomp filter, a hardened kernel with CR4.UMIP set, and a different vendor — and the first of those is worth chasing, because a seccomp filter that returns EPERM instead of a signal would break the harness's assumption that a refusal is a fault.)MAP_SHARED allocation with a plain global, exactly as the first version was, and re-run. (Expect every si_code to read 0 and the harness to fail on the #GP checks. Then read what the table now claims: “the kernel reported no code for any privileged instruction” is a sentence you could publish, and it is the sentence this bug produces. That is why the failure mode of a measurement harness is part of the measurement.)SIDT/SGDT/STR set to be the part that varies, because it depends on CR4.UMIP and a policy decision. A list of what a user program can do is a list of a kernel's configuration, and it is a list that changes between distributions.)/sys file, or a syscall return value — that is, a fact about the machine that the tool assumes rather than checks. Then check it, and if it is not there, say so. A tool that reports a number derived from a property it never verified is making a claim about the machine and has not said so.)Backwards, this concept has no single parent; it is the sum of the first eight, and the table it prints is the argument that the sum is a list rather than a pile. The privilege course's harness concept is where the one measurement this course builds on — SIDT returns and the IDT does not — was taken, and the difference is that this course made that asymmetry into fifty instructions and then into a partition the harness can check.
Forwards, and there is a course after this one. The fourth course in this section is about the data path, and it inherits a specific debt from the eighth concept here: the AVX-512 feature bits that read as all zero are the same zero that course has to work around, and the movdqa trap that retraction R2 caught is the same class of problem as a misaligned vector store being a fault rather than a slowdown. A course that teaches a data path on a machine that lacks half the instructions in it has to be explicit about which half, and this course is where the explicit part lives.
And the last thing, which is the limits block and which is not a footnote. The artifact prints nine things it cannot tell you, each one naming the instrument that would settle it:
$ ./sysdump | sed -n '/^ LIMITS/,/second machine/p' | head -40
LIMITS -- what this artifact cannot tell you, printed not footnoted,
and each one names the instrument that would settle it:
* ANY CYCLE COUNT. There is no counter this process can open:
RDPMC is a #GP, perf_event_open is EACCES, and the kernel
reports perf_event_paranoid above. Every duration in this file
is a DURATION, and a duration bounds a count without measuring
it. Settled by perf_event_open with a paranoid setting below 4
and a PMU passthrough from the host.
* WHETHER CR0 TO CR4 HOLD THE VALUES THE KERNEL REPORTS.
Section 2 prints arch_prctl's SMEP/SMAP/SHSTK bits and section
3 measures that every read of CR0-CR4 and CR0-CR8 is a fault.
A consequence is not a reading. Settled by a CPL 0 program.
Each limit ends in a sentence of the form settled by X, and that is the part worth copying. A limit that says “this was not measured” is a dead end. A limit that says “a twenty-line program at CPL 0 would settle it” is a work item, and it tells a reader exactly what they are missing in order to do the work themselves. Four of the nine gaps in this course are properties of the instrument rather than of the subject, and the last one — whether a second machine agrees — is the one no amount of cleverness on one machine can fix.
The verdict the artifact prints is the sentence to keep:
That last clause is the course's real conclusion and it is worth more than any table here. Four of the five things this artifact cannot tell you are limitations of a program running at CPL 3, and one of them — a second machine — is a limitation of being a program at all. Every claim in this collection that is marked INFERRED, every mechanism that is named rather than measured, and every QUOTED row in every table is the same fact: the measurement decides what may be claimed, and a course that hides which of its numbers are measurements is teaching a reader to trust the wrong sentences.