// Exercise the actual RegionMap declarations and method bodies, including the // actual Repeater/Room getLocalScopes wrappers through minimal role adapters. #include #include #include #include #include #include #include #include #include #include #include #include "region_under_test.inc" // Frozen original exporter from commit 3a3323c4d594dd766dea25caaad06bb5225a8ffa, // RegionMap.cpp SHA256 0f3dd22e2c228061334bcf95416cf40ecdd01310f99a011785a0837e43334900. // The adapter snapshots real public region state. The method below is unchanged // and is used only for >=2-byte legacy output compatibility, never for safety. namespace legacy { class RegionMap { uint16_t num_regions; RegionEntry regions[MAX_REGION_ENTRIES]; RegionEntry wildcard; public: explicit RegionMap(::RegionMap& source) : num_regions(source.getCount()), wildcard(source.getWildcard()) { for (int i = 0; i < num_regions; ++i) regions[i] = *source.getByIdx(i); } int exportNamesTo(char *dest, int max_len, uint8_t mask, bool invert = false); }; int RegionMap::exportNamesTo(char *dest, int max_len, uint8_t mask, bool invert) { char *dp = dest; // Check wildcard region bool wildcard_matches = invert ? (wildcard.flags & mask) : !(wildcard.flags & mask); if (wildcard_matches) { *dp++ = '*'; *dp++ = ','; } for (int i = 0; i < num_regions; i++) { auto region = ®ions[i]; // Check if region matches the filter criteria bool region_matches = invert ? (region->flags & mask) : !(region->flags & mask); if (region_matches) { int len = strlen(skip_hash(region->name)); if ((dp - dest) + len + 2 < max_len) { // only append if name will fit memcpy(dp, skip_hash(region->name), len); dp += len; *dp++ = ','; } } } if (dp > dest) { dp--; } // don't include trailing comma *dp = 0; // set null terminator return dp - dest; // return length } } // namespace legacy static void require(bool condition, const char* message) { if (!condition) { std::fprintf(stderr, "FAIL: %s\n", message); std::exit(1); } } static void populate(RegionMap& map, bool dense) { if (dense) { for (int i = 0; i < MAX_REGION_ENTRIES; ++i) { char name[31]; std::snprintf(name, sizeof(name), "#Place%02d", i); RegionEntry* entry = map.putRegion(name, 0); require(entry != nullptr, "populate full real RegionMap"); entry->flags = 0; } return; } const char* names[] = {"#Alpine", "$Secret", "B", "#Direct", "Both", "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234"}; const uint8_t flags[] = {0, 0, REGION_DENY_FLOOD, REGION_DENY_DIRECT, REGION_DENY_FLOOD | REGION_DENY_DIRECT, 0}; for (size_t i = 0; i < sizeof(names) / sizeof(names[0]); ++i) { RegionEntry* entry = map.putRegion(names[i], 0); require(entry != nullptr, "populate region names and filter policies"); entry->flags = flags[i]; } } static std::vector eligible(RegionMap& map, uint8_t mask, bool invert) { std::vector names; const auto matches = [=](uint8_t flags) { return invert ? (flags & mask) != 0 : (flags & mask) == 0; }; if (matches(map.getWildcard().flags)) names.emplace_back("*"); for (int i = 0; i < map.getCount(); ++i) { const RegionEntry* entry = map.getByIdx(i); if (matches(entry->flags)) names.emplace_back(RegionNameUtils::canonical(entry->name)); } return names; } static std::string joined(const std::vector& names) { std::string result; for (const auto& name : names) { if (!result.empty()) result += ','; result += name; } return result; } static void validate_output(const char* destination, int capacity, int count, const std::vector& names) { require(count >= 0 && count < capacity, "returned length is inside destination capacity"); require(destination[count] == 0, "returned length points to null terminator"); require(std::strlen(destination) == static_cast(count), "returned length is exactly the produced string length"); const std::string actual(destination); require(actual.empty() || (actual.front() != ',' && actual.back() != ','), "no leading or trailing comma"); size_t allowed = 0; for (size_t begin = 0; begin < actual.size();) { const size_t end = actual.find(',', begin); const std::string token = actual.substr(begin, end == std::string::npos ? end : end - begin); require(!token.empty(), "no empty region token"); while (allowed < names.size() && names[allowed] != token) ++allowed; require(allowed < names.size(), "only complete matching names in original order"); ++allowed; if (end == std::string::npos) break; begin = end + 1; } if (joined(names).size() + 3 < static_cast(capacity)) { require(actual == joined(names), "ample destination includes every matching name"); } } static void guarded_export(RegionMap& map, int capacity, uint8_t mask, bool invert) { constexpr size_t guard = 32; constexpr unsigned char sentinel = 0xA5; const size_t accessible = capacity > 0 ? static_cast(capacity) : 0; std::vector storage(guard + accessible + guard, sentinel); char* destination = reinterpret_cast(storage.data() + guard); const int count = map.exportNamesTo(destination, capacity, mask, invert); require(std::all_of(storage.begin(), storage.begin() + guard, [](unsigned char c) { return c == sentinel; }), "export preserved prefix guard"); require(std::all_of(storage.begin() + guard + accessible, storage.end(), [](unsigned char c) { return c == sentinel; }), "export wrote no byte beyond claimed destination capacity"); if (capacity <= 0) { require(count == 0, "nonpositive destination returns zero without writing"); } else { validate_output(destination, capacity, count, eligible(map, mask, invert)); if (capacity == 1) require(destination[0] == 0, "one-byte destination is empty"); if (capacity >= 2) { legacy::RegionMap original(map); std::vector baseline(accessible, sentinel); const int old_count = original.exportNamesTo( reinterpret_cast(baseline.data()), capacity, mask, invert); require(count == old_count && std::equal(baseline.begin(), baseline.end(), storage.begin() + guard), "all >=2-byte results and destination bytes match frozen original exporter"); } } } template static void guarded_wrapper(RegionMap& map, size_t capacity) { constexpr size_t guard = 32; constexpr unsigned char sentinel = 0xA5; std::vector storage(guard + capacity + guard, sentinel); char* destination = reinterpret_cast(storage.data() + guard); Role role(map); role.getLocalScopes(destination, capacity); require(std::all_of(storage.begin(), storage.begin() + guard, [](unsigned char c) { return c == sentinel; }), "role wrapper preserved prefix guard"); require(std::all_of(storage.begin() + guard + capacity, storage.end(), [](unsigned char c) { return c == sentinel; }), "role wrapper wrote no byte beyond claimed destination capacity"); if (capacity) { const void* end = std::memchr(destination, 0, capacity); require(end != nullptr, "role wrapper terminates inside supplied buffer"); validate_output(destination, static_cast(capacity), static_cast(end) - destination, eligible(map, REGION_DENY_FLOOD, false)); } } static void healthy() { TransportKeyStore store; RegionMap map(store); populate(map, false); for (int capacity : {96, 159, 160}) { char destination[160]; const char* golden = "*,Alpine,$Secret,Direct,ABCDEFGHIJKLMNOPQRSTUVWXYZ1234"; const int count = map.exportNamesTo(destination, capacity, REGION_DENY_FLOOD); require(std::strcmp(destination, golden) == 0 && count == static_cast(std::strlen(golden)), "healthy production-size output unchanged"); guarded_wrapper(map, capacity); guarded_wrapper(map, capacity); } RegionMap full(store); populate(full, true); const char* golden96 = "*,Place00,Place01,Place02,Place03,Place04,Place05,Place06,Place07,Place08,Place09,Place10"; const char* golden159 = "*,Place00,Place01,Place02,Place03,Place04,Place05,Place06,Place07,Place08,Place09,Place10,Place11,Place12,Place13,Place14,Place15,Place16,Place17,Place18"; for (int capacity : {96, 159, 160}) { char destination[160]; const char* golden = capacity == 96 ? golden96 : golden159; const int count = full.exportNamesTo(destination, capacity, REGION_DENY_FLOOD); require(std::strcmp(destination, golden) == 0 && count == static_cast(std::strlen(golden)), "full-map production-size truncation unchanged"); } } static void invalid() { TransportKeyStore store; RegionMap map(store); populate(map, false); for (int capacity : {INT_MIN, -4, -1, 0, 1, 2, 160, INT_MAX}) { require(map.exportNamesTo(nullptr, capacity, REGION_DENY_FLOOD) == 0, "null destination returns zero without access"); } for (int capacity : {INT_MIN, -4, -1, 0}) { guarded_export(map, capacity, REGION_DENY_FLOOD, false); } repeater::MyMesh repeater(map); room::MyMesh room(map); for (size_t capacity : {size_t(0), size_t(1), size_t(160)}) { repeater.getLocalScopes(nullptr, capacity); room.getLocalScopes(nullptr, capacity); } } static void matrix() { TransportKeyStore store; size_t cases = 0; for (int shape = 0; shape < 3; ++shape) { RegionMap map(store); if (shape) populate(map, shape == 2); for (uint8_t wildcard_flags = 0; wildcard_flags < 4; ++wildcard_flags) { map.getWildcard().flags = wildcard_flags; for (uint8_t mask = 0; mask < 4; ++mask) { for (bool invert : {false, true}) { for (int capacity = -1; capacity <= 320; ++capacity) { guarded_export(map, capacity, mask, invert); ++cases; } } } } } std::printf("PASS: %zu direct capacity/filter combinations\n", cases); } static void wrappers() { TransportKeyStore store; for (int shape = 0; shape < 3; ++shape) { RegionMap map(store); if (shape) populate(map, shape == 2); for (uint8_t wildcard_flags = 0; wildcard_flags < 4; ++wildcard_flags) { map.getWildcard().flags = wildcard_flags; for (size_t capacity = 0; capacity <= 320; ++capacity) { guarded_wrapper(map, capacity); guarded_wrapper(map, capacity); } } } } template static void one_byte() { TransportKeyStore store; RegionMap map(store); Role role(map); // An exact one-byte allocation makes an out-of-capacity intermediate comma // store observable to ASan even if the eventual terminator is otherwise valid. std::unique_ptr destination(new char[1]); destination[0] = '!'; role.getLocalScopes(destination.get(), 1); require(destination[0] == 0, "one-byte role wrapper produces an empty string"); } static void direct_one_byte() { TransportKeyStore store; RegionMap map(store); std::unique_ptr destination(new char[1]); destination[0] = '!'; require(map.exportNamesTo(destination.get(), 1, REGION_DENY_FLOOD) == 0, "one-byte direct exporter returns zero"); require(destination[0] == 0, "one-byte direct exporter produces an empty string"); } int main(int argc, char** argv) { require(argc == 2, "one scenario argument required"); const std::string scenario(argv[1]); if (scenario == "healthy") healthy(); else if (scenario == "invalid") invalid(); else if (scenario == "matrix") matrix(); else if (scenario == "wrappers") wrappers(); else if (scenario == "direct-one-byte") direct_one_byte(); else if (scenario == "repeater-one-byte") one_byte(); else if (scenario == "room-one-byte") one_byte(); else require(false, "known scenario"); std::printf("PASS: %s\n", scenario.c_str()); return 0; }