Live data from Hacker News

Destroying x86_64 instruction decoders with differential fuzzing

blog.trailofbits.com

41–50 of 113 posts

Re: Destroying x86_64 instruction decoders with differential fuzzing

#41
post #26

Earlier quoted context omitted.

The decoder doesn't actually take all that much space in the hardware, though. It's going to be smaller than the normal OoO logic, which means it's a pretty minor tax at best for actual hardware.

Saw die shots of Atom? Decoder makes more than half of the core, and there is no instruction cache. In a loop, you will be spending more joules on decoding than actual computations.

What die shots are you looking at? I'm looking at a Silverthorne die and most of the frontend is taken up by the L1I$.

https://en.wikichip.org/wiki/intel/microarchitectures/bonnel...

Re: Destroying x86_64 instruction decoders with differential fuzzing

#42

Wow, this a wonderfully rich post! I had a question about the following statement: >"In short, it’s a mess, with each generation adding and removing functionality, reusing or overloading instructions and instruction prefixes, and introducing increasingly complicated switching mechanisms between supported modes and privilege boundaries." Can someone elaborate on how a instruction at the machine level can be "overloade…

Thanks for the kind words!

> Can someone elaborate on how a instruction at the machine level can be "overloaded"? At this machine level how can an instruction be mapped to more than one entry in the microcode table?

Yep! Instruction overloading can occur in a few different senses:

1. As different valid permutations of operands and prefixes, e.g. `mov`

2. As having totally different functionalities in different privilege or architecture modes

3. As being repurposed entirely (e.g., inc/dec r32 are now REX prefixes)

Instruction-to-microcode translation is, unfortunately, not as simple as a (single) table lookup on x86_64 ;)

Re: Destroying x86_64 instruction decoders with differential fuzzing

#43
post #15

Tangent but: what's the additional overhead on a modern chip of parsing this crazy instruction set vs. a simpler to parse one like PPC64 or ARM64? Is it significant compared to all the other stuff that almost all modern CPUs do like out of order execution, register renaming, SIMD, virtualization, etc. etc. etc.? I've seen many people argue that it's significant but I never see anything in depth from anyone who really…

The variable length nature of the ISA is a big pain for out-of-order implementations. My understanding is that, to parse ahead in your instruction stream, you basically have to take every possible byte location starting at your PC and, in parallel, decode the instruction starting there. You'll eventually end up throwing away most of the results. Naturally, this costs a fair bit of power.

You only have to do enough to figure out instruction sizes from each byte, not the full decode.

RISC-V C and Arm Thumb have to to the same thing, albeit on 16-bit boundaries rather than byte boundaries.

Re: Destroying x86_64 instruction decoders with differential fuzzing

#44
post #39

>... a 40-year-old 16-bit ISA designed to be source-compatible with a 50-year-old 8-bit ISA. In fairness to the Intel of that era, they actually did a really good job with this. They gained basically zero warts from the 8080 assembler source compatibility. They mostly set out to make the best variable length 16 bit instruction set they could. They had significant competition at the time and they pretty much had to ma…

The 8086 introduced the abomination of segment registers. That created many software limitations for much of the 80's. Compilers with 64 K limits on array sizes, or code segment sizes, and similar. By comparison the 680x0 on classic Mac was a pleasure to program. A nice large simple flat address space.

Segment registers are the source of a bunch of C language rules about undefined behavior.

Re: Destroying x86_64 instruction decoders with differential fuzzing

#45
post #39

>... a 40-year-old 16-bit ISA designed to be source-compatible with a 50-year-old 8-bit ISA. In fairness to the Intel of that era, they actually did a really good job with this. They gained basically zero warts from the 8080 assembler source compatibility. They mostly set out to make the best variable length 16 bit instruction set they could. They had significant competition at the time and they pretty much had to ma…

The 8086 introduced the abomination of segment registers. That created many software limitations for much of the 80's. Compilers with 64 K limits on array sizes, or code segment sizes, and similar. By comparison the 680x0 on classic Mac was a pleasure to program. A nice large simple flat address space.

Segmentation is really nice, and should have been carried on, IMO. Half the issue with Spectre is that there isn't a clean way to describe to the processor different memory security contexts except with a page table pointer swap. Better segmentation support could have allowed you to sandbox memory without having to jump in and out of the kernel on transitions. Hence why VMWare, and Chrome's NaCL used segmentation hardware on x86-32 when they still could.

Re: Destroying x86_64 instruction decoders with differential fuzzing

#46

Reading even a bit about x86_64's complexity makes me see why Apple would want to move Macs to ARM (among other reasons.)

At this point, I think they're just waiting out the x86_64 patents and want to release their own core.

Re: Destroying x86_64 instruction decoders with differential fuzzing

#47
post #5

Earlier quoted context omitted.

Every time Intel has tried to more away from x86 (i960? Itanium? Maybe others...) they end up coming back. The years of backwards compatibility are a big selling point.

I really wish Itanium had taken off. IMO it is a superior architecture that was simply ahead of it's time. Wouldn't it be great if software instead of hardware, had complete control of instruction ordering? Wouldn't it be great to not be limited by the current SIMD restrictions? Wouldn't it be nice if you could choose to spend more compile time to get even faster programs (vs relying on the hardware to do it JIT)? I…

VLIW has ultimately failed several times outside of IA-64. It was briefly tried for GPUs too.

Re: Destroying x86_64 instruction decoders with differential fuzzing

#48
The x86 decoder is both simpler and more complex than it appears. I've played with the idea of creating the "worst" x86 decoder, which I classify as worst because its goal would not be to reproduce an assembly string but instead represent an oversimplified view of the instruction that makes sense semantically.

So the simple part of the decoding is that x86 instructions generally consist of an "opcode", a register number, and a third parameter which is either a register number, immediate, or memory operand. Occasionally, there is also another immediate operand tacked onto the instruction, based on the opcode. The REX prefix tacks on extra bits for the register number, the VEX prefix adds a third register number and provides a way to add some opcode bytes without spending extra bytes to do so, and the EVEX prefix has a few more extra operand types.

It's worth noting that if you're mapping semantics, some opcodes have the Mod/RM byte, but use fields in this parameter to add more bits to the opcode instead of as register numbers. If your semantics is prepared to handle per-register special cases (which it probably should, since x86 has per-register semantics in some cases), this isn't actually an issue.

The really difficult part, though, is managing prefixes. The new REX, VEX, and EVEX prefixes all have much tighter requirements on how they interact with each other: you can use exactly one of these prefixes, and the functionality in the older ones is completely subsumed by the newer ones. But the legacy prefixes don't have any such restrictions, and thus you can do annoying things like specify several of them at once or tack them on to instructions that don't need them.

The sanest way to handle prefixes, especially F0, F2, F3, 66, and 67, is to treat them instead as part of the opcode rather than an optional prefix, albeit ones that can float around a bit (although 66 is required to be the last prefix in a few cases). This also handles issues where the operand size override prefix doesn't actually override operand size. libbfd's hilariously bad results seem almost entirely due to doing the exact opposite of this rule, and not realizing that 66 and F0 actually create illegal instructions if not used correctly.

(Another thing you can do with weird prefixes is to just flag the result as "probably attempting to break a disassembler").

Unless you're interested in playing around with writing x86 decoders yourself, just use XED. It's maintained by Intel, and is up-to-date with all known instructions, and it should be capable of handling even most of the weirdest edge cases in handling unusual prefix combinations correctly. That said, it doesn't warn you of the few cases where the AMD x86 decoder and the Intel x86 decoder give inconsistent results.

Re: Destroying x86_64 instruction decoders with differential fuzzing

#49

Wow, this a wonderfully rich post! I had a question about the following statement: >"In short, it’s a mess, with each generation adding and removing functionality, reusing or overloading instructions and instruction prefixes, and introducing increasingly complicated switching mechanisms between supported modes and privilege boundaries." Can someone elaborate on how a instruction at the machine level can be "overloade…

Thanks for the kind words! > Can someone elaborate on how a instruction at the machine level can be "overloaded"? At this machine level how can an instruction be mapped to more than one entry in the microcode table? Yep! Instruction overloading can occur in a few different senses: 1. As different valid permutations of operands and prefixes, e.g. `mov` 2. As having totally different functionalities in different privil…

Thanks for the examples. This is helpful. I can't help but wonder if you or anyone else might be to elaborate on your last point:

>"Instruction-to-microcode translation is, unfortunately, not as simple as a (single) table lookup on x86_64 ;)"

Is the because of the overloading or are there other reasons it's not as simple as a LUT?

Cheers.

Re: Destroying x86_64 instruction decoders with differential fuzzing

#50

>... a 40-year-old 16-bit ISA designed to be source-compatible with a 50-year-old 8-bit ISA. In fairness to the Intel of that era, they actually did a really good job with this. They gained basically zero warts from the 8080 assembler source compatibility. They mostly set out to make the best variable length 16 bit instruction set they could. They had significant competition at the time and they pretty much had to ma…

z80 and 8086 were both garbage, next to the 68000.

It's sad x86 survived this far and is still so popular. Hopefully RISC-V will put an end to this hell.

Post reply on HN