fix(libScePosixForWebKit): keep the getservbyname result trivially destructible
The per-thread servent storage held std::string and std::vector members, so getservbyname_nid_postfix registered a thread_local destructor through __cxa_thread_atexit. On Windows that destructor runs after emulated TLS freed the object, and prx_thread_local_destructors (#4029) rejects libScePosixForWebKit.prx for it. The result now lives in a thread_local struct of fixed-size char arrays and an alias pointer array, sized for the longest name (submission), alias (www-http), protocol (sctp) and alias count in the embedded table; static_asserts check the sizes against the table and that the struct is trivially destructible. The guest servent layout and the per-thread result FreeBSD's getservent.c keeps in its NSS TLS state are unchanged. Nothing is allocated, so the ENOMEM path is gone. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
15bbc19501
commit
8b9529b99a
|
|
@ -10,12 +10,14 @@
|
|||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include "prx/libc/include/HostThreadLocal.hpp"
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cerrno>
|
||||
#include <new>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include "GuestResolver.hpp"
|
||||
|
||||
|
|
@ -58,13 +60,38 @@ constexpr ServiceRecord Services[]{
|
|||
{"pop3s", 995, "tcp", {"spop3"}}, {"pop3s", 995, "udp", {"spop3"}},
|
||||
};
|
||||
|
||||
constexpr std::size_t ServiceNameSize = 11;
|
||||
constexpr std::size_t ServiceAliasSize = 9;
|
||||
constexpr std::size_t ServiceProtocolSize = 5;
|
||||
constexpr std::size_t ServiceAliasCount = std::tuple_size_v<decltype(ServiceRecord::aliases)>;
|
||||
|
||||
template<typename TSelect>
|
||||
constexpr std::size_t LongestServiceText(TSelect select) {
|
||||
std::size_t longest = 0;
|
||||
for (const auto& service : Services)
|
||||
for (const char* text : select(service))
|
||||
if (text) longest = std::max(longest, std::char_traits<char>::length(text));
|
||||
return longest;
|
||||
}
|
||||
|
||||
static_assert(ServiceNameSize == LongestServiceText([](const ServiceRecord& record) { return std::array{record.name}; }) + 1);
|
||||
static_assert(ServiceAliasSize == LongestServiceText([](const ServiceRecord& record) { return record.aliases; }) + 1);
|
||||
static_assert(ServiceProtocolSize == LongestServiceText([](const ServiceRecord& record) { return std::array{record.protocol}; }) + 1);
|
||||
|
||||
struct ServiceLookup {
|
||||
GuestResolver::ServiceEntry entry{};
|
||||
std::string name;
|
||||
std::string protocol;
|
||||
std::vector<std::string> aliasNames;
|
||||
std::vector<char*> aliases;
|
||||
GuestResolver::ServiceEntry entry;
|
||||
std::array<char, ServiceNameSize> name;
|
||||
std::array<char, ServiceProtocolSize> protocol;
|
||||
std::array<std::array<char, ServiceAliasSize>, ServiceAliasCount> aliasNames;
|
||||
std::array<char*, ServiceAliasCount + 1> aliases;
|
||||
};
|
||||
static_assert(std::is_trivially_destructible_v<ServiceLookup>);
|
||||
|
||||
template<std::size_t Size>
|
||||
char* CopyServiceText(std::array<char, Size>& target, const char* text) {
|
||||
std::memcpy(target.data(), text, std::strlen(text) + 1);
|
||||
return target.data();
|
||||
}
|
||||
|
||||
bool NumericAddress(const char* name, std::array<unsigned char, 4>& address) {
|
||||
std::array<unsigned char, 3> parts{};
|
||||
|
|
@ -196,22 +223,17 @@ GuestResolver::ServiceEntry* APS5_VABI getservbyname_nid_postfix(const char* nam
|
|||
for (const char* alias : service.aliases)
|
||||
named = named || (alias && std::strcmp(name, alias) == 0);
|
||||
if (!named || (protocol && std::strcmp(protocol, service.protocol) != 0)) continue;
|
||||
try {
|
||||
static thread_local ServiceLookup lookup;
|
||||
lookup.name = service.name;
|
||||
lookup.protocol = service.protocol;
|
||||
lookup.aliasNames.clear();
|
||||
lookup.aliases.clear();
|
||||
for (const char* alias : service.aliases)
|
||||
if (alias) lookup.aliasNames.emplace_back(alias);
|
||||
for (auto& alias : lookup.aliasNames) lookup.aliases.push_back(alias.data());
|
||||
lookup.aliases.push_back(nullptr);
|
||||
lookup.entry = {lookup.name.data(), lookup.aliases.data(), htons(service.port), lookup.protocol.data()};
|
||||
return &lookup.entry;
|
||||
} catch (const std::bad_alloc&) {
|
||||
errno = ENOMEM;
|
||||
return nullptr;
|
||||
static thread_local ServiceLookup lookup;
|
||||
std::size_t count = 0;
|
||||
for (const char* alias : service.aliases) {
|
||||
if (!alias) continue;
|
||||
lookup.aliases[count] = CopyServiceText(lookup.aliasNames[count], alias);
|
||||
++count;
|
||||
}
|
||||
lookup.aliases[count] = nullptr;
|
||||
lookup.entry = {CopyServiceText(lookup.name, service.name), lookup.aliases.data(), htons(service.port),
|
||||
CopyServiceText(lookup.protocol, service.protocol)};
|
||||
return &lookup.entry;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue