Skip to content

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

Closed

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.

Activity

  1. added this to the 6.0.0 milestone on Jul 28, 2020
  2. removed
    untriagedNew issue has not been triaged by the area owner
    on Jul 28, 2020
  3. assigned and unassigned on Jul 28, 2020
  4. ghost locked as resolved and limited conversation to collaborators on Dec 8, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions