Skip to content

R2RDump Unwind code decoding logic missed the extra word or dword in the UWOP_ALLOC_LARGE case. #39940

@cshung

Description

@cshung

When we write the UnwindInfo for stack space allocation, depending on the size, we write an extra WORD/DWORD to the UnwindInfo stream as follow:

void Compiler::unwindAllocStackWindows(unsigned size)
{
assert(compGeneratingProlog);
FuncInfoDsc* func = funCurrentFunc();
assert(func->unwindHeader.Version == 1); // Can't call this before unwindBegProlog
assert(func->unwindHeader.CountOfUnwindCodes == 0); // Can't call this after unwindReserve
assert(size % 8 == 0); // Stack size is *always* 8 byte aligned
UNWIND_CODE* code;
if (size <= 128)
{
assert(func->unwindCodeSlot > sizeof(UNWIND_CODE));
code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(UNWIND_CODE)];
code->UnwindOp = UWOP_ALLOC_SMALL;
code->OpInfo = (size - 8) / 8;
}
else if (size <= 0x7FFF8)
{
assert(func->unwindCodeSlot > (sizeof(UNWIND_CODE) + sizeof(USHORT)));
USHORT* codedSize = (USHORT*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(USHORT)];
*codedSize = (USHORT)(size / 8);
code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(UNWIND_CODE)];
code->UnwindOp = UWOP_ALLOC_LARGE;
code->OpInfo = 0;
}
else
{
assert(func->unwindCodeSlot > (sizeof(UNWIND_CODE) + sizeof(ULONG)));
ULONG* codedSize = (ULONG*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(ULONG)];
*codedSize = size;
code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(UNWIND_CODE)];
code->UnwindOp = UWOP_ALLOC_LARGE;
code->OpInfo = 1;
}
unsigned int cbProlog = unwindGetCurrentOffset(func);
noway_assert((BYTE)cbProlog == cbProlog);
code->CodeOffset = (BYTE)cbProlog;
}

Notice how line 298 and line 307 moves the unwindCodeSlot further than just the usual sizeof(UNWIND_CODE).

However, the similar logic is missing from the decoder, here is the parsing code:

public UnwindCode(byte[] image, int index, ref int offset)
{
Index = index;
int off = offset;
CodeOffset = NativeReader.ReadByte(image, ref off);
byte op = NativeReader.ReadByte(image, ref off);
UnwindOp = (UnwindOpCodes)(op & 15);
OpInfo = (byte)(op >> 4);
OffsetLow = CodeOffset;
OffsetHigh = OpInfo;
FrameOffset = NativeReader.ReadUInt16(image, ref offset);
NextFrameOffset = -1;
IsOpInfo = false;

The size of the extra word/dword happens to be a multiple of 2, therefore these word/dword are misinterpreted as extra unwind opcode. This is just wrong. This explains why we saw more than one unwind opcode having the same code offset in R2RDump.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions