瀏覽代碼

Fix truncated map-generator Auth records

chnsw 3 天之前
父節點
當前提交
8f96fbb763

+ 6 - 7
Sunrise/src/middleware/bap/activity_message/activity_sensor_auth_bodies.cpp

@@ -51,15 +51,14 @@ constexpr std::size_t kSpawnKeyBits = 32 * 32 + 1 + 32;
  *
  * Slot 37 is schema `0x80805007` -> `0x80805008`, which holds two `0x8080500B` records and one
  * `0x80805009`. `0x8080500B` is 32 + 8 + `0x8080500F` (four groups of i8,i8,u32,bool = 196) + 7 + 1
- * + 32 + 32 + five biased i32 + `0x8080500D`; `0x80805009` is 32 + 8 + 8. `0x8080500D` is a 7-bit
- * COUNT followed by that many 16-bit elements, so **this body is variable width** -- a fixed number
- * cannot be right for it in general, and a zero count is the well-formed empty form.
+ * + 32 + 32 + five biased i32 + `0x8080500D`. With the dynamic arrays empty each is 475 bits.
+ * The final `0x80805009` contains a u32 plus `0x8080956C` and `0x8080954D`: fixed arrays of 32
+ * and 64 u8 elements. Their schema array lengths apply even when the dynamic arrays are empty.
  *
- * 2 x 475 + 48 = 998. An earlier note recorded 1750, which no whole element count produces
- * (23 gives 1734, 24 gives 1766); it was never verified on the wire the way the type-35 and
- * type-18 widths were, and it is not used.
+ * The empty body is 2 x 475 + 32 + 32 x 8 + 64 x 8 = 1750 bits. Counting each fixed array as
+ * one byte truncates it by 752 bits and prevents the client from applying the roster.
  */
-constexpr std::size_t kWideRecordBits = 998;
+constexpr std::size_t kWideRecordBits = 2 * 475 + 32 + 32 * 8 + 64 * 8;
 /**
  * Width of the type-30 body, from the client's field tables.
  * Slot 30 is schema `0x80809532`: a nested `0x80809C42` of {u32, 7-bit biased +1, 16-bit biased

+ 15 - 0
tests/CMakeLists.txt

@@ -18,3 +18,18 @@ else()
     target_compile_options(lore_visibility_test PRIVATE -Wall -Wextra -Werror -UNDEBUG)
 endif()
 add_test(NAME lore_visibility COMMAND lore_visibility_test)
+
+set(SRC "${CMAKE_CURRENT_SOURCE_DIR}/../Sunrise/src")
+add_executable(map_generator_auth_test map_generator_auth_test.cpp
+    "${SRC}/middleware/bap/activity_message/activity_sensor_auth_bodies.cpp"
+    "${SRC}/middleware/bap/activity_message/activity_sensor_auth_blocks.cpp"
+    "${SRC}/middleware/encoding/bit_reader.cpp"
+    "${SRC}/middleware/encoding/bit_writer.cpp")
+target_include_directories(map_generator_auth_test PRIVATE "${SRC}")
+target_compile_features(map_generator_auth_test PRIVATE cxx_std_20)
+if(MSVC)
+    target_compile_options(map_generator_auth_test PRIVATE /W4 /WX /UNDEBUG)
+else()
+    target_compile_options(map_generator_auth_test PRIVATE -Wall -Wextra -Werror -UNDEBUG)
+endif()
+add_test(NAME map_generator_auth COMMAND map_generator_auth_test)

+ 78 - 0
tests/map_generator_auth_test.cpp

@@ -0,0 +1,78 @@
+#include <array>
+#include <cassert>
+#include <cstdint>
+#include <cstdio>
+
+#include "core/logging/log.h"
+#include "middleware/bap/activity_message/sensor_auth_update.h"
+#include "middleware/encoding/bit_reader.h"
+
+namespace sunrise::core::log {
+bool accepts(Channel, Level) noexcept { return false; }
+void write(Channel, Level, std::string_view) noexcept {}
+}
+
+namespace bits = sunrise::middleware::encoding::bits;
+namespace auth = sunrise::middleware::bap::activity_message::sensor_auth_update;
+
+static std::uint64_t take(bits::Reader& reader, std::uint8_t width) {
+    std::uint64_t value{};
+    assert(reader.read(width, value));
+    return value;
+}
+
+// Independent wire walk of the empty native 86657 map-generator body. The trailing
+// schemas 0x8080956C and 0x8080954D have array lengths 32 and 64, not scalar u8s.
+static void empty_generator(bits::Reader& reader) {
+    for (int side = 0; side < 2; ++side) {
+        assert(take(reader, 32) == 0);
+        assert(take(reader, 8) == 0);
+        for (int anchor = 0; anchor < 4; ++anchor) {
+            assert(take(reader, 8) == 0);
+            assert(take(reader, 8) == 0);
+            assert(take(reader, 32) == 0);
+            assert(take(reader, 1) == 0);
+        }
+        assert(take(reader, 7) == 0);
+        assert(take(reader, 1) == 0);
+        assert(take(reader, 32) == 0);
+        assert(take(reader, 32) == 0);
+        for (int field = 0; field < 5; ++field) assert(take(reader, 32) == 0);
+        assert(take(reader, 7) == 0); // Dynamic array count; no elements follow.
+    }
+    assert(take(reader, 32) == 0);
+    for (int element = 0; element < 32; ++element) assert(take(reader, 8) == 0);
+    for (int element = 0; element < 64; ++element) assert(take(reader, 8) == 0);
+}
+
+int main() {
+    std::array<std::byte, 512> output{};
+    bits::Writer writer(output);
+    auth::Snapshot snapshot{};
+    snapshot.authorWideRecordBodies = true;
+    constexpr std::uint32_t key = 0x34D23982;
+    assert(auth::write_object_block(writer, snapshot, 0x8155213E, key, 37, 81,
+                                   auth::kSlotAuthFlag | auth::kSlotSenseFlag, false, false));
+    // A following nonzero field must not be consumed as part of the generator.
+    assert(writer.write(0xA5B6C7D8, 32));
+    std::size_t written{};
+    assert(writer.finish(written));
+    bits::Reader reader(std::span(output).first(written));
+    assert(take(reader, auth::kPresenceWidth) == 1);
+    assert(take(reader, auth::kKeyWidth) == key);
+    assert(take(reader, auth::kSlotTypeWidth) == 37 + auth::kSlotTypeBias);
+    assert(take(reader, auth::kSlotIndexWidth) == 81 + auth::kSlotIndexBias);
+    assert(take(reader, auth::kKeyWidth) == 1753); // Reset, root, body, absent sense.
+    assert(take(reader, 1) == 1);
+    assert(take(reader, 1) == 1);
+    empty_generator(reader);
+    assert(take(reader, 1) == 0);
+    assert(take(reader, 32) == 0xA5B6C7D8);
+    assert(reader.remaining_bits() < 8);
+
+    // The old 998-bit buffer must be refused, rather than reported as a complete body.
+    std::array<std::byte, 125> shortOutput{};
+    bits::Writer shortWriter(shortOutput);
+    assert(!auth::write_auth_body(shortWriter, snapshot, 37, false));
+    std::puts("map generator fixed arrays and following-field alignment passed");
+}