Skip to content

Fix rid lists when the target table has 0xFFFF rows - #591

Open
pcshrosbree wants to merge 1 commit into
0xd4d:masterfrom
pcshrosbree:fix/ridlist-65535-rows
Open

pcshrosbree wants to merge 1 commit into
0xd4d:masterfrom
pcshrosbree:fix/ridlist-65535-rows

Conversation

@pcshrosbree

Copy link
Copy Markdown

Fixes #590

When a list's target table has exactly 65,535 rows, CompressedMetadata.GetRidList returned an empty list for the last nonempty owner if two or more empty owners followed it. This PR treats a 0 in the next row's list column as the end of the table whenever the target table has 0xFFFF rows, not only when the next row is the source table's last row.

Why this shape: with 0xFFFF rows the column is normally 2 bytes wide, so the end-of-table value 0x10000 is stored as 0, and every trailing empty owner holds that 0. Their own lists stay empty, because GetRidList already returns an empty list when the start is 0. The tableDest.Rows == 0xFFFF test stays, so files whose target table has any other row count read as before. When it has exactly 0xFFFF rows, a 0 in the next row is now read as the end of the table, whatever wrote it. I haven't tested files that use 0 there for another purpose. ENCMetadata.GetRidList already treats any 0 as the end of the table.

What I ran (dnlib's net6.0 build, .NET SDK 10.0.401, Linux x64):

  • The repro in Parameters, fields or PDB locals read as empty when the table has exactly 65,535 rows #590 prints 65534 and 0 at master, and 65535 and 1 with this change.
  • I generated 36 assemblies with System.Reflection.Metadata's MetadataBuilder. The cases cover Param, Field and Portable PDB LocalVariable lists. For each list kind, I used 65,534, 65,535 and 65,536 target rows and zero to three trailing empty owners. I compared every owner's list with what was written. At master, the six 65,535-row files with two or three trailing empty owners lose the last nonempty owner's 7 items. With this change all 36 match.
  • I wrote the 12 Param files again with dnlib's own ModuleWriter. Master misreads 2 of them; this change reads all 12 correctly.

Read, not run: TypeDef.MethodList, EventMap, PropertyMap and Portable PDB local constants go through the same GetRidList, so this change covers them too. I didn't generate files for them.

dnlib has no test project, so this PR adds no test. The repro is in #590, and I can share the generator if it helps.

With 0xFFFF rows the list column is normally 2 bytes wide, so the
end-of-table value 0x10000 is stored as 0 in every row after the last
owner with items. GetRidList handled that 0 only when the next row was
the source table's last row. With two or more trailing empty owners,
the last owner with items got an empty list, so its params, fields or
PDB local variables were silently dropped.

Treat a 0 in the next row as the end of the table whenever the target
table has 0xFFFF rows. ENCMetadata.GetRidList already treats any 0 as
the end.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Parameters, fields or PDB locals read as empty when the table has exactly 65,535 rows

1 participant