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:
parent
73ac9ef7f4
commit
7a3ec9f015
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in New Issue