diff --git a/config/roblox_compatibility.json b/config/roblox_compatibility.json index 7cba285..bd82ac5 100644 --- a/config/roblox_compatibility.json +++ b/config/roblox_compatibility.json @@ -42,6 +42,17 @@ "allow_host_abi_bridges": true, "allow_host_constructor_replay": true, "reason": "Verified 2026-07-16 on the current host: independently signature-matched native allocator and pre-JNI state, 3479 constructors replayed without recovery, JNI_OnLoad and ASMA/V2 startup, 2988 Vulkan shaders, a real vkQueuePresentKHR, and explicit lifecycle teardown to Stopped; legacy executable patches and signal recovery remain disabled." + }, + { + "version_name": "2.734.917", + "version_code": 2908, + "elf_build_id": "63c5109637b7d7b2bdb8ed8f858023ff5ef49326", + "status": "supported", + "default_allowed": true, + "allow_legacy_binary_patches": false, + "allow_host_abi_bridges": true, + "allow_host_constructor_replay": true, + "reason": "Verified 2026-08-14 on the current host: the machine-derived exact-Build-ID profile passed two isolated no-recovery Tier C Vulkan canaries; legacy executable patches and signal recovery remain disabled." } ] } diff --git a/src/compat/host_abi_profile.cc b/src/compat/host_abi_profile.cc index 36185c5..0b3ae91 100644 --- a/src/compat/host_abi_profile.cc +++ b/src/compat/host_abi_profile.cc @@ -6,6 +6,31 @@ namespace mocktail::compat { namespace { constexpr HostAbiProfile kProfiles[] = { + // Roblox 2.734.917. This exact Build-ID profile was machine-derived from + // the verified reference and passed two isolated no-recovery Tier C Vulkan + // canaries with native constructors, JNI startup, presentation, and + // controlled lifecycle teardown. + {"63c5109637b7d7b2bdb8ed8f858023ff5ef49326", + {{{0x1cf69e2, HostBridgeKind::kAllocate, "small-allocate"}, + {0x1cf9c3a, HostBridgeKind::kUsableSize, "usable-size"}, + {0x21af33f, HostBridgeKind::kReallocate, "reallocate"}, + {0x1cf677e, HostBridgeKind::kAllocate, "allocate"}, + {0x1d12197, HostBridgeKind::kAlignedAllocate, + "aligned-allocate-direct"}, + {0x1cfa1d9, HostBridgeKind::kFree, "free"}}}, + 6, + {0x7571570, 0x767e428, 0x7ad47e8, 0x400, 0x1cf6df8, 0x1d82be7, + 0x7a10730, 0x7a107c0, 0x400000}, + {0x1cf677e, 0x1cfa1d9}, + 0x6f78a18, + 3556, + {{{2, 3}, {5, 3556}}}, + 2, + {{{2, 3556}}}, + 1, + 2, + {0x21b1889, 0x7a14898}, + HostAllocatorStrategy::kNativeMimalloc}, // Roblox 2.727.1199. Every function and state slot below was matched // independently against the previous supported payload by normalized // instruction signatures and RIP-relative references. The payload keeps diff --git a/tests/build_profile_test.cc b/tests/build_profile_test.cc index 03d7138..95682cf 100644 --- a/tests/build_profile_test.cc +++ b/tests/build_profile_test.cc @@ -62,6 +62,21 @@ TEST(BuildProfileTest, Payload2628IsSupportedWithoutBinaryPatches) { EXPECT_FALSE(result.profile->fmod_output_device_bridge.has_value()); } +TEST(BuildProfileTest, Payload2908IsSupportedWithoutBinaryPatches) { + const ProfileLookupResult result = FindBuildProfile( + kManifestPath, "63c5109637b7d7b2bdb8ed8f858023ff5ef49326"); + + ASSERT_TRUE(result) << result.error; + ASSERT_TRUE(result.profile.has_value()); + EXPECT_EQ(result.profile->version_name, "2.734.917"); + EXPECT_EQ(result.profile->version_code, 2908); + EXPECT_EQ(result.profile->status, BuildStatus::kSupported); + EXPECT_TRUE(result.profile->default_allowed); + EXPECT_FALSE(result.profile->allow_legacy_binary_patches); + EXPECT_TRUE(result.profile->allow_host_abi_bridges); + EXPECT_TRUE(result.profile->allow_host_constructor_replay); +} + TEST(BuildProfileTest, ReportsUnknownBuildWithoutInventingProfile) { const ProfileLookupResult result = FindBuildProfile( kManifestPath, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); diff --git a/tests/host_abi_profile_test.cc b/tests/host_abi_profile_test.cc index a40bf29..fa7c1fa 100644 --- a/tests/host_abi_profile_test.cc +++ b/tests/host_abi_profile_test.cc @@ -28,6 +28,30 @@ namespace mocktail::compat { namespace { +TEST(HostAbiProfileTest, Supported2908HasExactDerivedBoundaries) { + const HostAbiProfile* profile = + FindHostAbiProfile("63c5109637b7d7b2bdb8ed8f858023ff5ef49326"); + + ASSERT_NE(profile, nullptr); + ASSERT_EQ(profile->bridge_entry_count, 6U); + EXPECT_EQ(profile->bridge_entries[0].rva, 0x1cf69e2U); + EXPECT_EQ(profile->bridge_entries[5].rva, 0x1cfa1d9U); + EXPECT_EQ(profile->data_seeds.allocator_object_slot, 0x7571570U); + EXPECT_EQ(profile->native_allocator.allocate, 0x1cf677eU); + EXPECT_EQ(profile->native_allocator.deallocate, 0x1cfa1d9U); + EXPECT_EQ(profile->init_array_offset, 0x6f78a18U); + EXPECT_EQ(profile->init_array_count, 3556U); + EXPECT_TRUE(profile->HasValidConstructorRanges()); + EXPECT_EQ(profile->ConstructorRangeBegin(), 2U); + EXPECT_EQ(profile->ConstructorRangeEndExclusive(), 3556U); + EXPECT_TRUE(profile->HasValidNativeMimallocConstructorRanges()); + EXPECT_EQ(profile->native_pre_jni_bootstrap.registry_initializer, + 0x21b1889U); + EXPECT_EQ(profile->native_pre_jni_bootstrap.registry_slot, 0x7a14898U); + EXPECT_EQ(profile->default_allocator_strategy, + HostAllocatorStrategy::kNativeMimalloc); +} + TEST(HostAbiProfileTest, Supported2628HasExactResearchedBoundaries) { const HostAbiProfile* profile = FindHostAbiProfile("1686400865ae0e408cd7bd67de7a439625c6fd13");