From 7359406c163515b4e851cf7e3801e59f0d551834 Mon Sep 17 00:00:00 2001 From: tinysec Date: Thu, 9 Jul 2026 01:36:52 +0800 Subject: [PATCH] fix: TypeArchive.AddTypes passed the wrong struct to BNAddTypeArchiveTypes AddTypes marshaled a BNQualifiedNameTypeAndId (40 bytes: name, id, type) and passed it to BNAddTypeArchiveTypes, whose contract is BNQualifiedNameAndType (32 bytes: name, type). The 8-byte layout mismatch made core read the id pointer at offset 24 as the type handle, so no type was ever actually added (and multi-element arrays were fully mis-strided). Match Python TypeArchive.add_type/add_types (typearchive.py:225/222): callers supply only name and definition; the core assigns the id. AddTypes now takes QualifiedNameAndType[] and adds an AddType(name, type) convenience. The id-bearing QualifiedNameTypeAndId struct stays -- it is the correct model for the type-reading (BNGetTypeArchiveTypes) and analysis-definition (BNDefineAnalysisTypes) APIs. E2E: harness TypeArchiveAddTypesMarshalingTests (create archive, AddType, assert listable; AddTypes second, assert both persist). --- Handle/BNTypeArchive.cs | 44 +++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/Handle/BNTypeArchive.cs b/Handle/BNTypeArchive.cs index 94a6b7f..7ac6132 100644 --- a/Handle/BNTypeArchive.cs +++ b/Handle/BNTypeArchive.cs @@ -770,12 +770,15 @@ public unsafe QualifiedNameAndId[] GetTypeNamesAndIds(string snapshot = "") } /// - /// Adds an array of types to this type archive. - /// Each element carries a qualified name, a type ID, and the type itself. + /// + /// Adds an array of named types to this type archive, mirroring Python + /// TypeArchive.add_types (typearchive.py:222). The core assigns each type its id, so + /// callers supply only name and definition (the id-bearing BNQualifiedNameTypeAndId + /// struct belongs to the type-reading and analysis-definition APIs, not to add). /// - /// The array of qualified-name-type-and-id triples to add. + /// The array of qualified-name-and-type pairs to add. /// True if the types were added successfully; false otherwise. - public unsafe bool AddTypes(QualifiedNameTypeAndId[] types) + public unsafe bool AddTypes(QualifiedNameAndType[] types) { // 1. Validate the required parameter. if (null == types || 0 == types.Length) @@ -787,7 +790,7 @@ public unsafe bool AddTypes(QualifiedNameTypeAndId[] types) using (ScopedAllocator allocator = new ScopedAllocator()) { // 2.1 Convert each managed item to its native representation. - BNQualifiedNameTypeAndId[] nativeArray = new BNQualifiedNameTypeAndId[types.Length]; + BNQualifiedNameAndType[] nativeArray = new BNQualifiedNameAndType[types.Length]; for (int i = 0; i < types.Length; i++) { @@ -795,7 +798,7 @@ public unsafe bool AddTypes(QualifiedNameTypeAndId[] types) } // 2.2 Pin the native array and call the API. - IntPtr arrayPointer = allocator.AllocStructArray(nativeArray); + IntPtr arrayPointer = allocator.AllocStructArray(nativeArray); // 3. Forward to the native API. return NativeMethods.BNAddTypeArchiveTypes( @@ -806,6 +809,35 @@ public unsafe bool AddTypes(QualifiedNameTypeAndId[] types) } } + /// + /// Adds a single named type to this archive, mirroring Python + /// TypeArchive.add_type (typearchive.py:225). Convenience over + /// . + /// + /// The qualified name of the new type. + /// The definition of the new type. + /// True if the type was added successfully; false otherwise. + public bool AddType(QualifiedName name, BinaryNinja.Type type) + { + if (null == name) + { + throw new ArgumentNullException(nameof(name)); + } + + if (null == type) + { + throw new ArgumentNullException(nameof(type)); + } + + QualifiedNameAndType[] pair = new QualifiedNameAndType[] + { + new QualifiedNameAndType(name, type) + }; + + return this.AddTypes(pair); + } + + /// /// Retrieves all type names stored in this type archive at the given snapshot. ///