Skip to content

Handle unions in struct encoding parsing and harden offset scan - #140

Open
rootkiller6788 wants to merge 1 commit into
facebook:mainfrom
rootkiller6788:fix-struct-union-parsing
Open

Handle unions in struct encoding parsing and harden offset scan#140
rootkiller6788 wants to merge 1 commit into
facebook:mainfrom
rootkiller6788:fix-struct-union-parsing

Conversation

@rootkiller6788

Copy link
Copy Markdown

What

This fixes struct parsing when a struct contains a union, and hardens the struct-offset scan against type encodings NSGetSizeAndAlignment cannot handle.

1. Union parsing (FBStructEncodingParser.mm) - fixes #21

ObjC runtime type encodings represent a union as (name=...), or (?=...) for anonymous unions. The parser did not recognize ( as a grouping character: it fell through to the literal branch and stopped at the first quoted member name inside the union.

For a union with named members such as "someUnion"(?="x"i"y"f), the parser previously produced:

  • Type(name="someUnion", encoding="(?=")
  • Type(name="x", encoding="i")
  • Type(name="y", encoding="f)")

…treating overlapping union members as sequential struct fields and leaving a malformed (?= encoding. Every subsequent field's offset inside the struct was then wrong, so an object reference stored after the union was located at a garbage index (read of the wrong memory during cycle detection).

The fix adds _StringScanner::scanToClosingParen() and consumes the whole union as one opaque Type, keeping the offsets of the types that follow it correct.

2. Layout scan hardening (FBClassStrongLayout.mm) - related to #126

NSGetSizeAndAlignment does not throw on encodings it cannot size (bitfields b, C++ types, …): it logs the "unsupported type encoding spec" warning and returns NO. The code ignored the return value and then computed offset % align / offset += size from zero or garbage values. The scan now checks the return value and bails out (same intent as the existing @catch branch) instead of producing misaligned references.

Tests

Added to FBStructEncodingParserTests.mm:

  • testThatParserWillParseUnionAsSingleOpaqueType - literal encoding regression test asserting the exact parsed types.
  • testThatParserWillParseStructWithNamedUnionAndObject - real ivar encoding with a named union.
  • testThatParserWillParseStructWithAnonymousUnionAndObject - real ivar encoding with an anonymous union.

Verified with a standalone C++ build of the parser (ObjC bits mechanically stubbed) that runs the new union cases plus all pre-existing parser cases (primitives, objects, nested structs, bitfields, type paths) with no regressions.

The struct encoding parser treated a union (encoded by the runtime as
"(name=...)" or "(?=...)") as a plain literal, stopping at the first
quoted member name inside the union. For unions with named members this
produced a malformed encoding like "(?=" plus the union members as
sequential struct fields, which shifted every subsequent offset inside
the struct and mislocated (or missed) the strong references after it.
Consume a union as one opaque type instead (scanToClosingParen), so the
fields that follow a union are parsed and offset correctly.

While here, FBGetReferencesForObjectsInStructEncoding ignored the return
value of NSGetSizeAndAlignment, which does not throw on unsupported
encodings (e.g. bitfields or C++ types) - it logs a warning and returns
NO. The scan then computed offsets from zero/garbage size and alignment.
Bail out of the scan when the size and alignment cannot be obtained.

Adds parser tests covering named and anonymous unions followed by an
object, plus a literal-encoding regression test.
@meta-cla meta-cla Bot added the CLA Signed label Aug 21, 2026
@rootkiller6788
rootkiller6788 marked this pull request as ready for review August 26, 2026 15:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Struct parsing is flaky if unions are part of struct

1 participant