fix(relinker): decode UD1 as a trap

UD1 (0F B9 /r) always raises #UD, like UD2, but DecodeInstruction
reported it as sequential. Reachability analysis then followed the
bytes after it, so an import reachable only through that fallthrough
was kept by the strict unused NID filter. UD1 keeps its ModRM, so a
RIP-relative operand is still reported.
This commit is contained in:
Suryansh Prajapati 2026-10-08 13:06:44 +05:30
parent 73ac9ef7f4
commit 7a3ec9f015
3 changed files with 21 additions and 0 deletions

View File

@ -481,6 +481,12 @@ DecodedInstructionInfo X64InstructionDecoder::DecodeInstruction(
info.FlowKind = ControlFlowKind::Trap;
return info;
}
if (op == TwoByteUd1) {
if (pos < info.Length)
readModRm(pos, false);
info.FlowKind = ControlFlowKind::Trap;
return info;
}
if (op >= TwoByteJccRel32Min && op <= TwoByteJccRel32Max) {
std::int32_t disp = 0;
std::memcpy(&disp, data + pos, 4);

View File

@ -168,6 +168,9 @@ void decoderRipRelative() {
}
for (const auto& instruction : kRegisterVectorOperations)
require(!decoder.DecodeInstruction(instruction.data(), instruction.size()).HasRipRelativeDisp, "Vector instruction without a RIP-relative operand was reported as RIP-relative");
const Bytes ud1 = {0x0F, 0xB9, 0x05, 0x10, 0x00, 0x00, 0x00};
const auto trap = decoder.DecodeInstruction(ud1.data(), ud1.size());
require(trap.Length == ud1.size() && trap.FlowKind == Codegen::ControlFlowKind::Trap && trap.HasRipRelativeDisp && read<std::int32_t>(ud1, trap.RipRelativeDispOffset) == 0x10, "UD1 was not decoded as a trap with its RIP-relative operand");
}
void decoderTwoByteOpcodeLengths() {

View File

@ -68,6 +68,17 @@ void deadTailAndExplicitEntry() {
require(live.ImportSlots == std::set<std::uint64_t>{0x2000}, "Explicit entry after UD2 lost its import");
}
void ud1DeadTail() {
auto input = fixture();
emit(input, 0, {0x0F, 0xB9, 0xC0});
importThunk(input, 16, 0x2000);
require(AnalyzeStrictReachability(input).ImportSlots.empty(), "UD1 fallthrough retained a dead import");
input = fixture();
ripOperand(input, 0, {0x0F, 0xB9, 0x05}, 0x3000);
importThunk(input, 16, 0x2000);
require(AnalyzeStrictReachability(input).ImportSlots.empty(), "UD1 with a RIP-relative operand retained a dead import");
}
void conditionalTailCall() {
auto input = fixture();
emit(input, 0, {0x75, 0x0E, 0xC3});
@ -283,6 +294,7 @@ void filterAndPltCompaction() {
int main() {
try {
deadTailAndExplicitEntry();
ud1DeadTail();
conditionalTailCall();
callbackAndRelocationRoots();
registerImportCall();