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:
screamm 2026-10-12 13:16:38 +02:00 committed by Yossi Yadgar
parent 15bbc19501
commit 8b9529b99a
1 changed files with 42 additions and 20 deletions

View File

@ -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;
}