From 22a1c6ffa2af429a2cfc25a9ece69b19349ffe84 Mon Sep 17 00:00:00 2001 From: atharvjairath <54663702+atharvjairath@users.noreply.github.com> Date: Mon, 2 Nov 2026 13:45:38 +0530 Subject: [PATCH 1/4] feat(libSceLibreSsl3): implement RSA component ownership setters --- .../prx/libSceLibreSsl3/AdditionalExports.cpp | 16 -- core/libs/prx/libSceLibreSsl3/CMakeLists.txt | 14 ++ core/libs/prx/libSceLibreSsl3/include/Api.hpp | 8 +- core/libs/prx/libSceLibreSsl3/include/Key.hpp | 11 + .../prx/libSceLibreSsl3/src/RsaSetters.cpp | 80 +++++++ .../prx/libSceLibreSsl3/tests/RsaSetters.cpp | 217 ++++++++++++++++++ docs/dev/debt/libSceLibreSsl3.md | 12 +- 7 files changed, 332 insertions(+), 26 deletions(-) create mode 100644 core/libs/prx/libSceLibreSsl3/src/RsaSetters.cpp create mode 100644 core/libs/prx/libSceLibreSsl3/tests/RsaSetters.cpp diff --git a/core/libs/prx/libSceLibreSsl3/AdditionalExports.cpp b/core/libs/prx/libSceLibreSsl3/AdditionalExports.cpp index df0619e169..ec21114a5d 100644 --- a/core/libs/prx/libSceLibreSsl3/AdditionalExports.cpp +++ b/core/libs/prx/libSceLibreSsl3/AdditionalExports.cpp @@ -11,10 +11,6 @@ int APS5_VABI BIO_set_flags_nid_postfix() { NotImplemented_nid_no_patch(__func__); } -int APS5_VABI BN_clear_free_nid_postfix() { - NotImplemented_nid_no_patch(__func__); -} - int APS5_VABI BN_dup_nid_postfix() { NotImplemented_nid_no_patch(__func__); } @@ -260,10 +256,6 @@ int APS5_VABI ASN1_INTEGER_set_nid_postfix() { NotImplemented_nid_no_patch(__func__); } -int APS5_VABI RSA_set0_factors_nid_postfix() { - NotImplemented_nid_no_patch(__func__); -} - int APS5_VABI X509_set_pubkey_nid_postfix() { NotImplemented_nid_no_patch(__func__); } @@ -430,10 +422,6 @@ int APS5_VABI SSL_get0_verified_chain_nid_postfix() { NotImplemented_nid_no_patch(__func__); } -int APS5_VABI RSA_set0_crt_params_nid_postfix() { - NotImplemented_nid_no_patch(__func__); -} - int APS5_VABI EVP_PKEY_derive_nid_postfix() { NotImplemented_nid_no_patch(__func__); } @@ -645,10 +633,6 @@ int APS5_VABI EVP_aes_128_ccm_nid_postfix() { NotImplemented_nid_no_patch(__func__); } -int APS5_VABI RSA_set0_key_nid_postfix() { - NotImplemented_nid_no_patch(__func__); -} - int APS5_VABI tls_config_verify_client_nid_postfix() { NotImplemented_nid_no_patch(__func__); } diff --git a/core/libs/prx/libSceLibreSsl3/CMakeLists.txt b/core/libs/prx/libSceLibreSsl3/CMakeLists.txt index 99d71c09e2..335eab6368 100644 --- a/core/libs/prx/libSceLibreSsl3/CMakeLists.txt +++ b/core/libs/prx/libSceLibreSsl3/CMakeLists.txt @@ -145,3 +145,17 @@ if(BUILD_TESTING AND CMAKE_SYSTEM_NAME STREQUAL "Linux" AND EXISTS "${LIBS_INCLU -- $) set_tests_properties(guest_libressl3_mixed_evp PROPERTIES TIMEOUT 20) endif() + +target_sources(libSceLibreSsl3 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/RsaSetters.cpp) +if(BUILD_TESTING) + add_test_executable(guest_libressl3_rsa_setters_tests ${CMAKE_CURRENT_SOURCE_DIR}/tests/RsaSetters.cpp) + target_include_directories(guest_libressl3_rsa_setters_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_libressl3_rsa_setters_tests PRIVATE libSceLibreSsl3 libc) + configure_windows_unwind(guest_libressl3_rsa_setters_tests) + add_test(NAME guest_libressl3_rsa_setters COMMAND guest_libressl3_rsa_setters_tests) + set_tests_properties(guest_libressl3_rsa_setters PROPERTIES TIMEOUT 20) + if(WIN32) + set_tests_properties(guest_libressl3_rsa_setters PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() +endif() diff --git a/core/libs/prx/libSceLibreSsl3/include/Api.hpp b/core/libs/prx/libSceLibreSsl3/include/Api.hpp index cfd1bc9bdd..12b4857c23 100644 --- a/core/libs/prx/libSceLibreSsl3/include/Api.hpp +++ b/core/libs/prx/libSceLibreSsl3/include/Api.hpp @@ -122,7 +122,7 @@ unsigned char* APS5_VABI SHA1_nid_postfix(const unsigned char* data, std::size_t int APS5_VABI BIO_clear_flags_nid_postfix(); int APS5_VABI BIO_set_flags_nid_postfix(); -int APS5_VABI BN_clear_free_nid_postfix(); +void APS5_VABI BN_clear_free_nid_postfix(LibreSsl3Bignum* number); int APS5_VABI BN_dup_nid_postfix(); int APS5_VABI ERR_func_error_string_nid_postfix(); int APS5_VABI EVP_CIPHER_iv_length_nid_postfix(const LibreSsl3EvpCipher* cipher); @@ -191,7 +191,7 @@ int APS5_VABI Unknown_3KZ_minus_L4y1gak_nid_no_patch(); int APS5_VABI EVP_chacha20_poly1305_nid_postfix(); int APS5_VABI EVP_chacha20_nid_postfix(); int APS5_VABI ASN1_INTEGER_set_nid_postfix(); -int APS5_VABI RSA_set0_factors_nid_postfix(); +int APS5_VABI RSA_set0_factors_nid_postfix(LibreSsl3Rsa* rsa, LibreSsl3Bignum* p, LibreSsl3Bignum* q); int APS5_VABI X509_set_pubkey_nid_postfix(); int APS5_VABI BIO_get_shutdown_nid_postfix(); int APS5_VABI tls_accept_socket_nid_postfix(); @@ -232,7 +232,7 @@ int APS5_VABI Unknown_PBYz0kpXwg8_nid_no_patch(); int APS5_VABI X509_new_nid_postfix(); int APS5_VABI RSA_get0_factors_nid_postfix(); int APS5_VABI SSL_get0_verified_chain_nid_postfix(); -int APS5_VABI RSA_set0_crt_params_nid_postfix(); +int APS5_VABI RSA_set0_crt_params_nid_postfix(LibreSsl3Rsa* rsa, LibreSsl3Bignum* dmp1, LibreSsl3Bignum* dmq1, LibreSsl3Bignum* iqmp); int APS5_VABI EVP_PKEY_derive_nid_postfix(); int APS5_VABI EVP_PKEY_sign_nid_postfix(); int APS5_VABI X509_get_signature_nid_nid_postfix(); @@ -285,7 +285,7 @@ int APS5_VABI EC_GROUP_cmp_nid_postfix(); int APS5_VABI EVP_PKEY_sign_init_nid_postfix(); int APS5_VABI EC_POINT_dup_nid_postfix(); int APS5_VABI EVP_aes_128_ccm_nid_postfix(); -int APS5_VABI RSA_set0_key_nid_postfix(); +int APS5_VABI RSA_set0_key_nid_postfix(LibreSsl3Rsa* rsa, LibreSsl3Bignum* n, LibreSsl3Bignum* e, LibreSsl3Bignum* d); int APS5_VABI tls_config_verify_client_nid_postfix(); int APS5_VABI EVP_PKEY_cmp_nid_postfix(); int APS5_VABI d2i_ASN1_SEQUENCE_ANY_nid_postfix(); diff --git a/core/libs/prx/libSceLibreSsl3/include/Key.hpp b/core/libs/prx/libSceLibreSsl3/include/Key.hpp index 4dac85fd57..3c1fd15250 100644 --- a/core/libs/prx/libSceLibreSsl3/include/Key.hpp +++ b/core/libs/prx/libSceLibreSsl3/include/Key.hpp @@ -3,6 +3,17 @@ #include #include +struct LibreSsl3Bignum { + std::uint64_t* data; + std::int32_t top; + std::int32_t capacity; + std::int32_t negative; + std::int32_t flags; +}; + +static_assert(sizeof(LibreSsl3Bignum) == 24); +static_assert(offsetof(LibreSsl3Bignum, flags) == 20); + struct LibreSsl3EvpPkey { std::int32_t type; std::int32_t references; diff --git a/core/libs/prx/libSceLibreSsl3/src/RsaSetters.cpp b/core/libs/prx/libSceLibreSsl3/src/RsaSetters.cpp new file mode 100644 index 0000000000..84d03ae3b7 --- /dev/null +++ b/core/libs/prx/libSceLibreSsl3/src/RsaSetters.cpp @@ -0,0 +1,80 @@ +#include "prx/libSceLibreSsl3/libSceLibreSsl3.hpp" +#include "prx/libc/include/Api.hpp" +#include + +namespace { + +constexpr int Allocated = 1; +constexpr int StaticData = 2; + +void Validate_nid_no_patch(const LibreSsl3Bignum* number) { + if (number && number->data && !(number->flags & StaticData) && number->capacity < 0) + throw std::invalid_argument("LibreSSL3 BIGNUM has a negative limb capacity"); +} + +void Wipe_nid_no_patch(void* pointer, std::size_t bytes) { + auto* output = static_cast(pointer); + for (std::size_t i = 0; i < bytes; ++i) output[i] = 0; +} + +void ValidateReplacement_nid_no_patch(void* previous, const LibreSsl3Bignum* replacement) { + if (replacement) Validate_nid_no_patch(static_cast(previous)); +} + +void Replace_nid_no_patch(void*& previous, LibreSsl3Bignum* replacement) { + if (!replacement) return; + BN_clear_free_nid_postfix(static_cast(previous)); + previous = replacement; +} + +} + +extern "C" { + +void APS5_VABI BN_clear_free_nid_postfix(LibreSsl3Bignum* number) { + if (!number) return; + Validate_nid_no_patch(number); + const bool allocated = (number->flags & Allocated) != 0; + if (number->data && !(number->flags & StaticData)) { + Wipe_nid_no_patch(number->data, static_cast(number->capacity) * sizeof(*number->data)); + free_nid_postfix(number->data); + } + Wipe_nid_no_patch(number, sizeof(*number)); + if (allocated) free_nid_postfix(number); +} + +int APS5_VABI RSA_set0_key_nid_postfix(LibreSsl3Rsa* rsa, LibreSsl3Bignum* n, LibreSsl3Bignum* e, LibreSsl3Bignum* d) { + if (!rsa) throw std::invalid_argument("RSA_set0_key requires a key"); + if ((!rsa->n && !n) || (!rsa->e && !e)) return 0; + ValidateReplacement_nid_no_patch(rsa->n, n); + ValidateReplacement_nid_no_patch(rsa->e, e); + ValidateReplacement_nid_no_patch(rsa->d, d); + Replace_nid_no_patch(rsa->n, n); + Replace_nid_no_patch(rsa->e, e); + Replace_nid_no_patch(rsa->d, d); + return 1; +} + +int APS5_VABI RSA_set0_factors_nid_postfix(LibreSsl3Rsa* rsa, LibreSsl3Bignum* p, LibreSsl3Bignum* q) { + if (!rsa) throw std::invalid_argument("RSA_set0_factors requires a key"); + if ((!rsa->p && !p) || (!rsa->q && !q)) return 0; + ValidateReplacement_nid_no_patch(rsa->p, p); + ValidateReplacement_nid_no_patch(rsa->q, q); + Replace_nid_no_patch(rsa->p, p); + Replace_nid_no_patch(rsa->q, q); + return 1; +} + +int APS5_VABI RSA_set0_crt_params_nid_postfix(LibreSsl3Rsa* rsa, LibreSsl3Bignum* dmp1, LibreSsl3Bignum* dmq1, LibreSsl3Bignum* iqmp) { + if (!rsa) throw std::invalid_argument("RSA_set0_crt_params requires a key"); + if ((!rsa->dmp1 && !dmp1) || (!rsa->dmq1 && !dmq1) || (!rsa->iqmp && !iqmp)) return 0; + ValidateReplacement_nid_no_patch(rsa->dmp1, dmp1); + ValidateReplacement_nid_no_patch(rsa->dmq1, dmq1); + ValidateReplacement_nid_no_patch(rsa->iqmp, iqmp); + Replace_nid_no_patch(rsa->dmp1, dmp1); + Replace_nid_no_patch(rsa->dmq1, dmq1); + Replace_nid_no_patch(rsa->iqmp, iqmp); + return 1; +} + +} diff --git a/core/libs/prx/libSceLibreSsl3/tests/RsaSetters.cpp b/core/libs/prx/libSceLibreSsl3/tests/RsaSetters.cpp new file mode 100644 index 0000000000..bb28917d12 --- /dev/null +++ b/core/libs/prx/libSceLibreSsl3/tests/RsaSetters.cpp @@ -0,0 +1,217 @@ +#include "prx/libSceLibreSsl3/libSceLibreSsl3.hpp" +#include "prx/libc/include/Api.hpp" +#include "prx/libc/include/ApplicationHeap.hpp" +#include +#include +#include +#include +#include +#include +#include + +using SetKey = int (APS5_VABI*)(LibreSsl3Rsa*, LibreSsl3Bignum*, LibreSsl3Bignum*, LibreSsl3Bignum*); +using SetFactors = int (APS5_VABI*)(LibreSsl3Rsa*, LibreSsl3Bignum*, LibreSsl3Bignum*); +using ClearFree = void (APS5_VABI*)(LibreSsl3Bignum*); +static_assert(std::is_same_v); +static_assert(std::is_same_v); +static_assert(std::is_same_v); +static_assert(std::is_same_v); + +namespace { + +std::size_t checks = 0, cases = 0, frees = 0; +struct Allocation { void* pointer; std::size_t size; }; +std::array allocations{}; + +void Require(bool condition, std::source_location where = std::source_location::current()) { + ++checks; + if (condition) return; + std::fprintf(stderr, "LibreSSL3 RSA ownership check failed at line %u\n", where.line()); + std::abort(); +} + +bool IsLive(const void* pointer) { + if (!pointer) return false; + for (const auto& allocation : allocations) + if (allocation.pointer == pointer) return true; + return false; +} + +void* APS5_VABI Allocate_nid_no_patch(std::size_t bytes) { + void* result = std::malloc(bytes + 16); + Require(result != nullptr); + std::memset(result, 0xa5, bytes + 16); + for (auto& allocation : allocations) { + if (allocation.pointer) continue; + allocation = {result, bytes}; + return result; + } + std::abort(); +} + +void APS5_VABI Free_nid_no_patch(void* pointer) { + if (!pointer) return; + for (auto& allocation : allocations) { + if (allocation.pointer != pointer) continue; + const auto* bytes = static_cast(pointer); + for (std::size_t i = 0; i < allocation.size; ++i) Require(bytes[i] == 0); + for (std::size_t i = allocation.size; i < allocation.size + 16; ++i) Require(bytes[i] == 0xa5); + allocation = {}; + ++frees; + std::free(pointer); + return; + } + Require(false); +} + +void* APS5_VABI NoCalloc_nid_no_patch(std::size_t, std::size_t) { std::abort(); } +void* APS5_VABI NoRealloc_nid_no_patch(void*, std::size_t) { std::abort(); } +void* APS5_VABI NoAlign_nid_no_patch(std::size_t, std::size_t) { std::abort(); } +void* APS5_VABI NoRealign_nid_no_patch(void*, std::size_t, std::size_t) { std::abort(); } +int APS5_VABI NoPosixAlign_nid_no_patch(void**, std::size_t, std::size_t) { std::abort(); } + +LibreSsl3Bignum* Number(int capacity) { + auto* number = static_cast(malloc_nid_postfix(sizeof(LibreSsl3Bignum))); + Require(number != nullptr); + auto* data = static_cast(malloc_nid_postfix(capacity * sizeof(std::uint64_t))); + Require(data != nullptr); + for (int i = 0; i < capacity; ++i) data[i] = 0xfedcba9876543210ULL - i; + *number = {data, capacity ? 1 : 0, capacity, 1, 1 | 4}; + return number; +} + +void Clean() { + for (const auto& allocation : allocations) Require(allocation.pointer == nullptr); +} + +template void Throws(TCall call) { + bool threw = false; + try { call(); } catch (const std::invalid_argument&) { threw = true; } + Require(threw); +} + +using Field = void* LibreSsl3Rsa::*; +void Matrix(int group) { + const std::array fields = group == 0 + ? std::array{&LibreSsl3Rsa::n, &LibreSsl3Rsa::e, &LibreSsl3Rsa::d} + : group == 1 ? std::array{&LibreSsl3Rsa::p, &LibreSsl3Rsa::q, nullptr} + : std::array{&LibreSsl3Rsa::dmp1, &LibreSsl3Rsa::dmq1, &LibreSsl3Rsa::iqmp}; + const int count = group == 1 ? 2 : 3; + const int required = group == 2 ? 7 : 3; + for (int oldMask = 0; oldMask < (1 << count); ++oldMask) { + for (int newMask = 0; newMask < (1 << count); ++newMask) { + ++cases; + struct Guarded { std::uint64_t before; LibreSsl3Rsa key; std::uint64_t after; } guarded; + std::memset(&guarded, 0xa5, sizeof(guarded)); + std::array old{}, next{}; + std::array input{}; + for (int i = 0; i < count; ++i) { + if (oldMask & (1 << i)) old[i] = Number(i + 2); + if (newMask & (1 << i)) { next[i] = Number(i + 1); input[i] = *next[i]; } + guarded.key.*fields[i] = old[i]; + } + Guarded expected; + std::memcpy(&expected, &guarded, sizeof(expected)); + const bool accepted = ((oldMask | newMask) & required) == required; + const auto before = frees; + std::size_t expectedFrees = 0; + if (accepted) { + for (int i = 0; i < count; ++i) { + if (!next[i]) continue; + expected.key.*fields[i] = next[i]; + if (old[i]) expectedFrees += 2; + } + } + const int result = group == 0 ? RSA_set0_key_nid_postfix(&guarded.key, next[0], next[1], next[2]) + : group == 1 ? RSA_set0_factors_nid_postfix(&guarded.key, next[0], next[1]) + : RSA_set0_crt_params_nid_postfix(&guarded.key, next[0], next[1], next[2]); + Require(result == int(accepted)); + Require(std::memcmp(&guarded, &expected, sizeof(guarded)) == 0); + Require(frees - before == expectedFrees); + for (int i = 0; i < count; ++i) { + if (next[i]) { + Require(IsLive(next[i])); + Require(std::memcmp(next[i], &input[i], sizeof(input[i])) == 0); + Require(next[i]->data[0] == 0xfedcba9876543210ULL); + } + if (IsLive(old[i])) BN_clear_free_nid_postfix(old[i]); + BN_clear_free_nid_postfix(next[i]); + } + Clean(); + } + } +} + +void Disposal() { + BN_clear_free_nid_postfix(nullptr); + for (int capacity : {0, 1, 7}) { + BN_clear_free_nid_postfix(Number(capacity)); + Clean(); + } + for (int flags : {0, 2, 4, 6}) { + std::array borrowed{11, 22, 33, 44}; + const auto expected = borrowed; + const bool isStatic = (flags & 2) != 0; + LibreSsl3Bignum number{isStatic ? borrowed.data() : static_cast(malloc_nid_postfix(sizeof(borrowed))), 1, 4, 1, flags}; + BN_clear_free_nid_postfix(&number); + const LibreSsl3Bignum zero{}; + Require(std::memcmp(&number, &zero, sizeof(zero)) == 0); + Require(borrowed == expected); + Clean(); + } + std::array borrowed{0x12345678, 0xabcdef}; + auto* number = static_cast(malloc_nid_postfix(sizeof(LibreSsl3Bignum))); + *number = {borrowed.data(), 2, -1, 1, 3}; + BN_clear_free_nid_postfix(number); + Require(borrowed[0] == 0x12345678 && borrowed[1] == 0xabcdef); + Clean(); + LibreSsl3Bignum empty{nullptr, -1, -1, 1, 0}; + BN_clear_free_nid_postfix(&empty); + const LibreSsl3Bignum zero{}; + Require(std::memcmp(&empty, &zero, sizeof(zero)) == 0); +} + +void Invalid() { + Throws([] { RSA_set0_key_nid_postfix(nullptr, nullptr, nullptr, nullptr); }); + Throws([] { RSA_set0_factors_nid_postfix(nullptr, nullptr, nullptr); }); + Throws([] { RSA_set0_crt_params_nid_postfix(nullptr, nullptr, nullptr, nullptr); }); + std::uint64_t limb = 0x1234; + LibreSsl3Bignum bad{&limb, 1, -1, 0, 0}; + const auto original = bad; + Throws([&] { BN_clear_free_nid_postfix(&bad); }); + Require(std::memcmp(&bad, &original, sizeof(bad)) == 0 && limb == 0x1234); + auto* old = Number(2); + auto* first = Number(3); + auto* second = Number(4); + LibreSsl3Rsa rsa{}; + rsa.n = rsa.p = rsa.dmp1 = old; + rsa.e = rsa.q = rsa.dmq1 = &bad; + rsa.iqmp = &bad; + const auto before = rsa; + const auto beforeFrees = frees; + Throws([&] { RSA_set0_key_nid_postfix(&rsa, first, second, nullptr); }); + Throws([&] { RSA_set0_factors_nid_postfix(&rsa, first, second); }); + Throws([&] { RSA_set0_crt_params_nid_postfix(&rsa, first, nullptr, second); }); + Require(std::memcmp(&rsa, &before, sizeof(rsa)) == 0 && frees == beforeFrees); + BN_clear_free_nid_postfix(old); + BN_clear_free_nid_postfix(first); + BN_clear_free_nid_postfix(second); + Clean(); +} + +} + +int main() { + void* const api[10]{reinterpret_cast(Allocate_nid_no_patch), reinterpret_cast(Free_nid_no_patch), + reinterpret_cast(NoCalloc_nid_no_patch), reinterpret_cast(NoRealloc_nid_no_patch), + reinterpret_cast(NoAlign_nid_no_patch), reinterpret_cast(NoRealign_nid_no_patch), + reinterpret_cast(NoPosixAlign_nid_no_patch)}; + ApplicationHeapRegister_nid_no_patch(api); + Matrix(0); + Matrix(1); + Matrix(2); + Disposal(); + Invalid(); + Clean(); + std::printf("RSA ownership: %zu matrix cases, %zu checks, %zu cleared allocations, zero live allocations\n", cases, checks, frees); +} diff --git a/docs/dev/debt/libSceLibreSsl3.md b/docs/dev/debt/libSceLibreSsl3.md index b7835765eb..dfc36be7f1 100644 --- a/docs/dev/debt/libSceLibreSsl3.md +++ b/docs/dev/debt/libSceLibreSsl3.md @@ -32,7 +32,7 @@ ### Additional callable export inventory -- [Additional exports](../../../core/libs/prx/libSceLibreSsl3/AdditionalExports.cpp) declare 175 callable exports: 165 named and 10 known only by NID. Every entry throws. Parameterless `int` declarations are unknown-signature placeholders, not verified ABI contracts. Names/NIDs match the 13.60 export table. No functional implementation or title unblock is claimed. +- [Additional exports](../../../core/libs/prx/libSceLibreSsl3/AdditionalExports.cpp) declare 171 remaining callable exports: 161 named and 10 known only by NID. Every entry throws. Parameterless `int` declarations are unknown-signature placeholders, not verified ABI contracts. Names/NIDs match the 13.60 export table. No functional implementation or title unblock is claimed. | Name | NID | | --- | --- | @@ -53,7 +53,6 @@ | `BIO_clear_flags` | `3sypCuBDdQs` | | `EVP_chacha20` | `3tGgkjBQ82A` | | `ASN1_INTEGER_set` | `3v2Omd4sqcU` | -| `RSA_set0_factors` | `3wlK0tLONvk` | | `X509_set_pubkey` | `409ieWBMPUk` | | `BIO_get_shutdown` | `44M+SEbpNdQ` | | `tls_accept_socket` | `4Q3bvOy4LY8` | @@ -73,7 +72,6 @@ | `unknown` | `A-Hs-8mau+8` | | `ERR_load_X509_strings` | `A0lZtFjhcTw` | | `tls_config_set_key_mem` | `A65b+ZhOt9k` | -| `BN_clear_free` | `ABcJv8S1KEM` | | `unknown` | `AiNud8LR8B4` | | `X509_cmp_current_time` | `Ax+Uu++rnrA` | | `SSL_CTX_set_timeout` | `BBOgwZLvXHA` | @@ -112,7 +110,6 @@ | `X509_new` | `PNfwr3s6dEs` | | `RSA_get0_factors` | `PcUZS7XHwHk` | | `SSL_get0_verified_chain` | `QKIOxHfZLh8` | -| `RSA_set0_crt_params` | `QTV9Nyi7ryg` | | `EVP_PKEY_derive` | `QYEjNnn6PT4` | | `SSL_CIPHER_get_id` | `QaiPrM-RrrM` | | `EVP_PKEY_sign` | `QhYzE+Jdvuk` | @@ -190,7 +187,6 @@ | `EC_POINT_dup` | `uITUdhDDAg8` | | `HKDF` | `uJ2ZXq4z3As` | | `EVP_aes_128_ccm` | `uM9UnC8KFjY` | -| `RSA_set0_key` | `uihDBp74nLI` | | `EVP_PKEY_CTX_free` | `ulzAPWCIBkw` | | `tls_free` | `uz3bWkbHZu4` | | `tls_config_verify_client` | `v1vHv4jOJxQ` | @@ -212,4 +208,8 @@ | `ECDSA_SIG_get0` | `z8fsXF8JuL0` | | `EVP_MD_block_size` | `zCa75KLWWn4` | -- 115 of the 126 exports the 13.60 table lists only by NID carry, in the table above and in [Additional exports](../../../core/libs/prx/libSceLibreSsl3/AdditionalExports.cpp), the LibreSSL names whose `tools/nid_names.py` hash equals the NID; the names were taken from the identifiers of the LibreSSL 3.8.4 and 4.0.0 `include`, `crypto`, `ssl` and `tls` sources. A name match does not show that the console function has LibreSSL's prototype or behavior; each keeps the no-parameter import-resolution convention and throws through `NotImplemented_nid_no_patch`. `AES_wrap_key`, `ECDSA_SIG_free`, `ECDSA_do_verify`, `ERR_load_ERR_strings`, `ERR_print_errors_fp` and `i2d_PUBKEY` also hash to the NIDs of six `unknown` rows, which keep their NID names; `PBYz0kpXwg8` hashes to no identifier of those sources. +- 112 of the 122 remaining exports the 13.60 table lists only by NID carry, in the table above and in [Additional exports](../../../core/libs/prx/libSceLibreSsl3/AdditionalExports.cpp), the LibreSSL names whose `tools/nid_names.py` hash equals the NID; the names were taken from the identifiers of the LibreSSL 3.8.4 and 4.0.0 `include`, `crypto`, `ssl` and `tls` sources. A name match does not show that the console function has LibreSSL's prototype or behavior; each keeps the no-parameter import-resolution convention and throws through `NotImplemented_nid_no_patch`. `AES_wrap_key`, `ECDSA_SIG_free`, `ECDSA_do_verify`, `ERR_load_ERR_strings`, `ERR_print_errors_fp` and `i2d_PUBKEY` also hash to the NIDs of six `unknown` rows, which keep their NID names; `PBYz0kpXwg8` hashes to no identifier of those sources. + +### Functional + +- [RSA_set0_key, RSA_set0_factors, RSA_set0_crt_params and BN_clear_free](../../../core/libs/prx/libSceLibreSsl3/src/RsaSetters.cpp) have no console verification or key-constructor/destructor interoperability measurement. They use the existing 152-byte RSA layout and the 24-byte BIGNUM layout of [LibreSSL 4.0.0](https://github.com/libressl/openbsd/tree/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto), following `rsa/rsa_lib.c` and `bn/bn_lib.c`: a successful setter owns supplied components, retains omitted components and clears replaced components through the guest allocator; static limb arrays are borrowed. Missing required components return zero before mutation. Null RSA objects and negative owned limb capacities throw instead of dereferencing invalid memory or erasing an unbounded range. Malformed pointers and aliased component ownership are unverified. Closes when: compared with the console and complete key lifetimes. From bc7b197f78b96e5976cdb2bdca62be48ff3b7925 Mon Sep 17 00:00:00 2001 From: atharvjairath <54663702+atharvjairath@users.noreply.github.com> Date: Mon, 2 Nov 2026 17:39:14 +0530 Subject: [PATCH 2/4] docs(libSceLibreSsl3): clarify BIGNUM allocator coupling --- docs/dev/debt/libSceLibreSsl3.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/dev/debt/libSceLibreSsl3.md b/docs/dev/debt/libSceLibreSsl3.md index dfc36be7f1..d3787b2261 100644 --- a/docs/dev/debt/libSceLibreSsl3.md +++ b/docs/dev/debt/libSceLibreSsl3.md @@ -213,3 +213,5 @@ ### Functional - [RSA_set0_key, RSA_set0_factors, RSA_set0_crt_params and BN_clear_free](../../../core/libs/prx/libSceLibreSsl3/src/RsaSetters.cpp) have no console verification or key-constructor/destructor interoperability measurement. They use the existing 152-byte RSA layout and the 24-byte BIGNUM layout of [LibreSSL 4.0.0](https://github.com/libressl/openbsd/tree/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto), following `rsa/rsa_lib.c` and `bn/bn_lib.c`: a successful setter owns supplied components, retains omitted components and clears replaced components through the guest allocator; static limb arrays are borrowed. Missing required components return zero before mutation. Null RSA objects and negative owned limb capacities throw instead of dereferencing invalid memory or erasing an unbounded range. Malformed pointers and aliased component ownership are unverified. Closes when: compared with the console and complete key lifetimes. + +- [Forwarded BN_new and BN_free](../../../core/libs/prx/libSceLibreSsl3/ParentExports.cpp) still throw in the parent library. Future implementations must allocate owned BIGNUM limbs and structs from the same guest heap used by the RSA setters and set `BN_FLG_MALLOCED` on heap-owned structs. Constructor/setter/destructor interoperability remains unverified. From 1b58e90f4f4e4ea36b23a8aace19364cab6fc1a0 Mon Sep 17 00:00:00 2001 From: Adria Espi Date: Thu, 5 Nov 2026 20:38:24 +0100 Subject: [PATCH 3/4] feat(libSceNKWebKit): implement WKOpenPanelResultListener functions --- core/libs/prx/libSceNKWebKit/Export.cpp | 6 -- .../include/InventoryExports.hpp | 2 - .../include/WebKitValueExports.hpp | 2 + .../src/OpenPanelResultListener.cpp | 36 ++++++++ core/libs/prx/libSceNKWebKit/src/Types.hpp | 6 ++ core/libs/prx/libSceNKWebKit/tests/Base.cpp | 89 +++++++++++++++++++ docs/dev/debt/libSceNKWebKit.md | 11 ++- 7 files changed, 140 insertions(+), 12 deletions(-) create mode 100644 core/libs/prx/libSceNKWebKit/src/OpenPanelResultListener.cpp diff --git a/core/libs/prx/libSceNKWebKit/Export.cpp b/core/libs/prx/libSceNKWebKit/Export.cpp index b2ecc459d8..6d4b981f34 100644 --- a/core/libs/prx/libSceNKWebKit/Export.cpp +++ b/core/libs/prx/libSceNKWebKit/Export.cpp @@ -14390,13 +14390,7 @@ int APS5_VABI WKOpenPanelParametersGetAllowsMultipleFiles() { NotImplemented_nid_no_patch(__func__); } -int APS5_VABI WKOpenPanelResultListenerCancel() { - NotImplemented_nid_no_patch(__func__); -} -int APS5_VABI WKOpenPanelResultListenerChooseFiles() { - NotImplemented_nid_no_patch(__func__); -} int APS5_VABI WKPageAreScrollbarAnimationsSuppressed() { NotImplemented_nid_no_patch(__func__); diff --git a/core/libs/prx/libSceNKWebKit/include/InventoryExports.hpp b/core/libs/prx/libSceNKWebKit/include/InventoryExports.hpp index 1294a580d9..962616f517 100644 --- a/core/libs/prx/libSceNKWebKit/include/InventoryExports.hpp +++ b/core/libs/prx/libSceNKWebKit/include/InventoryExports.hpp @@ -531,8 +531,6 @@ int APS5_VABI WKOpenPanelParametersCopySelectedFileNames(); int APS5_VABI WKOpenPanelParametersGetAllowsDirectories(); int APS5_VABI WKOpenPanelParametersGetAllowsMultipleFiles(); int APS5_VABI WKOpenPanelParametersGetMediaCaptureType(); -int APS5_VABI WKOpenPanelResultListenerCancel(); -int APS5_VABI WKOpenPanelResultListenerChooseFiles(); int APS5_VABI WKPageAreScrollbarAnimationsSuppressed(); int APS5_VABI WKPageBackgroundExtendsBeyondPage(); int APS5_VABI WKPageBeginPrinting(); diff --git a/core/libs/prx/libSceNKWebKit/include/WebKitValueExports.hpp b/core/libs/prx/libSceNKWebKit/include/WebKitValueExports.hpp index 6593983c7b..4c302f175e 100644 --- a/core/libs/prx/libSceNKWebKit/include/WebKitValueExports.hpp +++ b/core/libs/prx/libSceNKWebKit/include/WebKitValueExports.hpp @@ -104,6 +104,8 @@ std::uint32_t APS5_VABI WKNotificationGetTypeID(); std::uint32_t APS5_VABI WKNotificationManagerGetTypeID(); std::uint32_t APS5_VABI WKNotificationPermissionRequestGetTypeID(); std::uint32_t APS5_VABI WKOpenPanelParametersGetTypeID(); +void APS5_VABI WKOpenPanelResultListenerCancel(const void* listenerRef); +void APS5_VABI WKOpenPanelResultListenerChooseFiles(const void* listenerRef, const void* fileURLsRef, const void* allowedMimeTypesRef); std::uint32_t APS5_VABI WKOpenPanelResultListenerGetTypeID(); std::uint32_t APS5_VABI WKPageConfigurationGetTypeID(); std::uint32_t APS5_VABI WKPageGetTypeID(); diff --git a/core/libs/prx/libSceNKWebKit/src/OpenPanelResultListener.cpp b/core/libs/prx/libSceNKWebKit/src/OpenPanelResultListener.cpp new file mode 100644 index 0000000000..f49ec753d4 --- /dev/null +++ b/core/libs/prx/libSceNKWebKit/src/OpenPanelResultListener.cpp @@ -0,0 +1,36 @@ +#include "prx/libSceNKWebKit/src/Types.hpp" +#include "prx/libSceNKWebKit/include/WebKitValueExports.hpp" + +extern "C" { + +void APS5_VABI WKOpenPanelResultListenerCancel(const void* listenerRef) { + auto* listener = NKWebKit::Cast(listenerRef, __func__); + listener->cancelled = true; + listener->chosenFiles.clear(); +} + +void APS5_VABI WKOpenPanelResultListenerChooseFiles(const void* listenerRef, const void* fileURLsRef, const void* allowedMimeTypesRef) { + auto* listener = NKWebKit::Cast(listenerRef, __func__); + listener->cancelled = false; + listener->chosenFiles.clear(); + const auto* array = NKWebKit::CastOrNull(fileURLsRef, __func__); + if (array) { + for (const auto& item : array->elements) { + if (const auto* url = NKWebKit::CastOrNull(item.Get(), __func__)) { + std::string path = url->text; + if (path.rfind("file://", 0) == 0) { + path = path.substr(7); + } + listener->chosenFiles.push_back(NKWebKit::String::FromUtf8(path.c_str())); + } else if (const auto* str = NKWebKit::CastOrNull(item.Get(), __func__)) { + std::string path = str->Utf8(); + if (path.rfind("file://", 0) == 0) { + path = path.substr(7); + } + listener->chosenFiles.push_back(NKWebKit::String::FromUtf8(path.c_str())); + } + } + } +} + +} diff --git a/core/libs/prx/libSceNKWebKit/src/Types.hpp b/core/libs/prx/libSceNKWebKit/src/Types.hpp index cc32e388d0..c4e87d71bc 100644 --- a/core/libs/prx/libSceNKWebKit/src/Types.hpp +++ b/core/libs/prx/libSceNKWebKit/src/Types.hpp @@ -114,4 +114,10 @@ public: bool isContentEditable = false; }; +class OpenPanelResultListener final : public ObjectOf { +public: + bool cancelled = false; + std::vector> chosenFiles; +}; + } // namespace NKWebKit diff --git a/core/libs/prx/libSceNKWebKit/tests/Base.cpp b/core/libs/prx/libSceNKWebKit/tests/Base.cpp index bd48d40b46..598a3af1d1 100644 --- a/core/libs/prx/libSceNKWebKit/tests/Base.cpp +++ b/core/libs/prx/libSceNKWebKit/tests/Base.cpp @@ -429,6 +429,48 @@ static void TestHitTestResult() { WKRelease(ref); } +static void TestOpenPanelResultListener() { + Require(WKOpenPanelResultListenerGetTypeID() == 80); + + auto listener = NKWebKit::Make(); + const void* ref = listener.Get(); + + Require(!listener->cancelled); + Require(listener->chosenFiles.empty()); + + WKOpenPanelResultListenerCancel(ref); + Require(listener->cancelled); + + auto urlObj = NKWebKit::Make("file:///path/to/file.txt"); + const void* urlValues[] = { urlObj.Get() }; + const void* fileArray = WKArrayCreate(urlValues, 1); + + WKOpenPanelResultListenerChooseFiles(ref, fileArray, nullptr); + Require(!listener->cancelled); + Require(listener->chosenFiles.size() == 1); + Require(listener->chosenFiles[0]->Utf8() == "/path/to/file.txt"); + WKRelease(fileArray); + + WKOpenPanelResultListenerChooseFiles(ref, nullptr, nullptr); + Require(listener->chosenFiles.empty()); + + bool threw = false; + try { + WKOpenPanelResultListenerCancel(nullptr); + } catch (const std::invalid_argument&) { + threw = true; + } + Require(threw); + + threw = false; + try { + WKOpenPanelResultListenerChooseFiles(nullptr, nullptr, nullptr); + } catch (const std::invalid_argument&) { + threw = true; + } + Require(threw); +} + static void TestCertificateInfo() { Require(WKCertificateInfoGetTypeID() == 0); Require(WKCertificateInfoCreateWithCertficateChain(nullptr) == nullptr); @@ -452,6 +494,53 @@ int main() { TestURLRequestResponse(); TestError(); TestHitTestResult(); + ath/to/file.txt"); + WKRelease(fileArray); + + WKOpenPanelResultListenerChooseFiles(ref, nullptr, nullptr); + Require(listener->chosenFiles.empty()); + + bool threw = false; + try { + WKOpenPanelResultListenerCancel(nullptr); + } catch (const std::invalid_argument&) { + threw = true; + } + Require(threw); + + threw = false; + try { + WKOpenPanelResultListenerChooseFiles(nullptr, nullptr, nullptr); + } catch (const std::invalid_argument&) { + threw = true; + } + Require(threw); +} + +static void TestCertificateInfo() { + Require(WKCertificateInfoGetTypeID() == 0); + Require(WKCertificateInfoCreateWithCertficateChain(nullptr) == nullptr); + Require(WKCertificateInfoGetVerificationError(nullptr) == 0); + Require(WKCertificateInfoCopyVerificationErrorDescription(nullptr) == nullptr); + Require(WKCertificateInfoGetCertificateChainSize(nullptr) == 0); + Require(WKCertificateInfoCopyCertificateAtIndex(nullptr, 0) == nullptr); +} + +int main() { + TestTypeIDs(); + TestSecurityOrigin(); + TestRetainRelease(); + TestStrings(); + TestURL(); + TestArray(); + TestDictionary(); + TestData(); + TestNumbers(); + TestGeometry(); + TestUserContentURLPattern(); + TestURLRequestResponse(); + TestError(); + TestOpenPanelResultListener(); TestCertificateInfo(); std::printf("All libSceNKWebKit Base tests passed!\n"); return 0; diff --git a/docs/dev/debt/libSceNKWebKit.md b/docs/dev/debt/libSceNKWebKit.md index 6ab3e588c7..2c3044c7f9 100644 --- a/docs/dev/debt/libSceNKWebKit.md +++ b/docs/dev/debt/libSceNKWebKit.md @@ -18,7 +18,7 @@ Inventory prose retains historical counts; use the [generated progress report](h - [WTF language exports](../../../core/libs/prx/libSceNKWebKit/src/WtfLanguage.cpp) call language change observers in registration order; the console iterates a `HashMap` keyed by context in hash-table order. The 16-byte `Vector` layout and the rest of the behavior follow the [PS5 11.x WebKit source](https://github.com/neonmodder123/PS5-WebKit-11.x/blob/80f77e94c63ee7202eec3d4b5b1a021dd89011ba/Source/WTF/wtf/Language.cpp) and have no console verification. Null and `-1` contexts and null observers throw instead of reproducing undefined behavior. Closes when: compared with a console module. ### Functional -- [HitTestResult getters](../../../core/libs/prx/libSceNKWebKit/src/HitTestResult.cpp) (libSceNKWebKit) - WKHitTestResult* getters return image/link/media/PDF URLs, link text, bounding box and content editable flag from the host object. +- [Additional callable exports](../../../core/libs/prx/libSceNKWebKit/Export.cpp) - 7367 of the 8703 inventory declarations listed below throw through `NotImplemented_nid_no_patch`; signatures and behavior are unverified. The later 13.60-labelled image contains all 8703; 8448 match the public 3.20 catalogue, while 255 are absent and their presence in retail 3.20 is unverified. Per-image records show the latter in a separate 3.20-labelled image, but neither image label is authenticated. Identified names match NIDs in the [public symbol database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv) or [supplemental catalog](https://github.com/claimore22/ps5rs/blob/2304be5131221bae0dac24b9767cabffda33f747/data/nids.csv). Data exports need separate analysis. - [Constant-return exports](../../../core/libs/prx/libSceNKWebKit/src/ConstantExports.cpp) (libSceNKWebKit) - 137 exports answer with a fixed value in the PS5 11.00 module: 111 execute `xor eax,eax; ret`, 13 `mov eax,imm32; ret` and 13 `mov al,1; ret` (addresses and bodies from the 11.00 `libSceNKWebKit.sprx`, SHA-256 `3af992253665387feeb5df90ff27dff683e3aa270cc4d92a0535b46ab18fa20d`; NIDs recomputed from the declared names). The 124 EAX-writing bodies measure the 32-bit return; the 13 `mov al,1` bodies write only the low byte, so for them only AL is measured and the upper bits are unspecified. The declared prototypes stay the no-parameter `int` placeholders, so no other width, pointer or floating-point return is claimed. Only 11.00 was measured. - [Security origins](../../../core/libs/prx/libSceNKWebKit/src/URL.cpp) (libSceNKWebKit) - `WKSecurityOriginCopyDatabaseIdentifier` still throws pending `encodeForFileName` (#14858). `WKSecurityOriginCreateFromString` and `WKSecurityOriginCreateFromDatabaseIdentifier` still throw; URL parsing and default-port normalization (#13766) are not wired into origin creation. Value objects follow [the pinned WebKit source](https://github.com/neonmodder123/PS5-WebKit-11.x/tree/80f77e94c63ee7202eec3d4b5b1a021dd89011ba/Source); console execution and ABI are unverified. @@ -43,7 +43,8 @@ Inventory prose retains historical counts; use the [generated progress report](h - The [revision-pinned identity inventory](https://gist.github.com/kyon-eth/d44995f181e1818977bd38f0e6fc0f34/8afcb14b0707a09b6274ff7f7bcf33ef46cfe2e7) records the module, library, symbol type and NIDs for the declarations in [Export.cpp](../../../core/libs/prx/libSceNKWebKit/Export.cpp); it is a metadata summary rather than independent verification. For the 255 absent from the public catalogue, [per-image ELF records](https://gist.github.com/kyon-eth/147be8dd556e9cced2577ccbed548972/1a659ecfe819e724675991a574a1aebb5fbb48e5) include the raw qualified names, module and library identities, symbol type, values and sizes; the record file SHA-256 is `44bdc4d6073192a8f41b7be359b75364b3d41dbbd11ce649924b8cebdd4ffe51`. Image labels are not authenticated. -- [Additional callable exports](../../../core/libs/prx/libSceNKWebKit/Export.cpp) - 7366 of the 8703 inventory declarations listed below throw through `NotImplemented_nid_no_patch`; signatures and behavior are unverified. The later 13.60-labelled image contains all 8703; 8448 match the public 3.20 catalogue, while 255 are absent and their presence in retail 3.20 is unverified. Per-image records show the latter in a separate 3.20-labelled image, but neither image label is authenticated. Identified names match NIDs in the [public symbol database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv) or [supplemental catalog](https://github.com/claimore22/ps5rs/blob/2304be5131221bae0dac24b9767cabffda33f747/data/nids.csv). Data exports need separate analysis. +- [Additional callable exports](../../../core/libs/prx/libSceNKWebKit/Export.cpp) - 7364 of the 8703 inventory declarations listed below throw through `NotImplemented_nid_no_patch`; signatures and behavior are unverified. The later 13.60-labelled image contains all 8703; 8448 match the public 3.20 catalogue, while 255 are absent and their presence in retail 3.20 is unverified. Per-image records show the latter in a separate 3.20-labelled image, but neither image label is authenticated. Identified names match NIDs in the [public symbol database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv) or [supplemental catalog](https://github.com/claimore22/ps5rs/blob/2304be5131221bae0dac24b9767cabffda33f747/data/nids.csv). Data exports need separate analysis. +>>>>>>> 4108e01b31 (feat(libSceNKWebKit): implement WKOpenPanelResultListener functions) | Name | NID | | --- | --- | @@ -507,8 +508,6 @@ Inventory prose retains historical counts; use the [generated progress report](h | `WKOpenPanelParametersCopySelectedFileNames` | `Oa8DwUsT8yg` | | `WKOpenPanelParametersGetAllowsDirectories` | `yZ8MpxQizWM` | | `WKOpenPanelParametersGetAllowsMultipleFiles` | `6JUqQUan7cM` | -| `WKOpenPanelResultListenerCancel` | `O0YiYsUaleQ` | -| `WKOpenPanelResultListenerChooseFiles` | `2ChSmFDC91I` | | `WKPageAreScrollbarAnimationsSuppressed` | `OQYQvowtC6I` | | `WKPageBackgroundExtendsBeyondPage` | `qKJ2G1pGKmU` | | `WKPageBeginPrinting` | `hujPhSbtd4A` | @@ -7427,5 +7426,9 @@ Inventory prose retains historical counts; use the [generated progress report](h - [WTF::FileSystemImpl::encodeForFileName, decodeFromFilename and lastComponentOfPathIgnoringTrailingSlash](../../../core/libs/prx/libSceNKWebKit/src/WtfString.cpp) - are source-guided by pinned [FileSystem.cpp](https://github.com/WebKit/WebKit/blob/c55364d53f97/Source/WTF/wtf/FileSystem.cpp#L66-L210), not console-measured; the device's WebKit revision is unverified. A null `String` reference, or an encoded result above 2^31-1 units, throws where WebKit has undefined behaviour or a release assertion. - [WTF decimal parsing](../../../core/libs/prx/libSceNKWebKit/CharactersToNumber.cpp) - the device's WebKit revision is unverified; `charactersToDouble`, `charactersToFloat` and `StringToDoubleConverter` follow pinned WebKit [toDoubleType](https://github.com/WebKit/WebKit/blob/c55364d53f97/Source/WTF/wtf/text/WTFString.cpp#L1040-L1098) and [StringToIeee](https://github.com/WebKit/WebKit/blob/c55364d53f97/Source/WTF/wtf/dtoa/double-conversion.cc#L459-L664). Under a rounding mode other than round-to-nearest, WebKit's [exact-power fast path](https://github.com/WebKit/WebKit/blob/c55364d53f97/Source/WTF/wtf/dtoa/strtod.cc#L219-L254) and the double-to-float cast in [Strtof](https://github.com/WebKit/WebKit/blob/c55364d53f97/Source/WTF/wtf/dtoa/strtod.cc#L485-L588) round in the current mode and the host conversion may round differently. - [WTF decimal parsing limits](../../../core/libs/prx/libSceNKWebKit/CharactersToNumber.cpp) - inputs of about 1 GiB or more whose decimal exponent leaves the range of WebKit's [`int` exponent](https://github.com/WebKit/WebKit/blob/c55364d53f97/Source/WTF/wtf/dtoa/double-conversion.cc#L488-L490) throw, where WebKit overflows. A UTF-16 `charactersTo*` length above `UINT32_MAX` after leading spaces throws where WebKit's [conversion Vector](https://github.com/WebKit/WebKit/blob/c55364d53f97/Source/WTF/wtf/Vector.h#L291-L293) crashes. +<<<<<<< HEAD - [WTF StringBuilder](../../../core/libs/prx/libSceNKWebKit/src/StringBuilder.cpp) (libSceNKWebKit) - throws where WebKit crashes: an overflow of a builder that crashes on overflow, a length sum above `UINT32_MAX`, `toString` after an overflow, and null characters with a nonzero length. It also throws where a debug assertion guards memory WebKit would corrupt: `reserveCapacity` on an empty 16-bit builder without a buffer, and [`appendQuotedJSONString`](https://github.com/WebKit/WebKit/blob/c55364d53f97/Source/WTF/wtf/text/StringBuilderJSON.cpp#L127-L128) of a 16-bit string into an 8-bit builder that holds an adopted string and no buffer. A capacity above the tree's `StringImpl` limit records an overflow, as a failed allocation does. Growing or shrinking a buffer with one reference copies it instead of calling `fastRealloc`, so its address always changes. - [WTF TextStream pointers](../../../core/libs/prx/libSceNKWebKit/src/TextStream.cpp) (libSceNKWebKit) - `operator<<(const void*)` formats the pointer as the tree's libc formats `%p` ([`snprintf`](https://github.com/WebKit/WebKit/blob/c55364d53f97/Source/WTF/wtf/text/TextStream.cpp#L114-L119) in WebKit); the console libc's output is unverified. +======= +- [WKOpenPanelResultListener functions](../../../core/libs/prx/libSceNKWebKit/src/OpenPanelResultListener.cpp) (libSceNKWebKit) - nothing in the library creates a WKOpenPanelResultListener (no UI client requests open panels); stored results are not consumed by a file picker dialog; upstream invalidates the listener after the first call, and a null file array is treated as empty. +>>>>>>> 3e08de0518 (feat(libSceNKWebKit): implement WKOpenPanelResultListener functions) From 70c3307bad3ece2840854096c4beced80f663502 Mon Sep 17 00:00:00 2001 From: Dean Galvin Date: Fri, 6 Nov 2026 16:57:41 +1300 Subject: [PATCH 4/4] test(libSceNKWebKit): remove leftover conflict fragments from the Base test and debt notes --- core/libs/prx/libSceNKWebKit/tests/Base.cpp | 46 --------------------- docs/dev/debt/libSceNKWebKit.md | 6 +-- 2 files changed, 1 insertion(+), 51 deletions(-) diff --git a/core/libs/prx/libSceNKWebKit/tests/Base.cpp b/core/libs/prx/libSceNKWebKit/tests/Base.cpp index 598a3af1d1..255b8325f7 100644 --- a/core/libs/prx/libSceNKWebKit/tests/Base.cpp +++ b/core/libs/prx/libSceNKWebKit/tests/Base.cpp @@ -494,52 +494,6 @@ int main() { TestURLRequestResponse(); TestError(); TestHitTestResult(); - ath/to/file.txt"); - WKRelease(fileArray); - - WKOpenPanelResultListenerChooseFiles(ref, nullptr, nullptr); - Require(listener->chosenFiles.empty()); - - bool threw = false; - try { - WKOpenPanelResultListenerCancel(nullptr); - } catch (const std::invalid_argument&) { - threw = true; - } - Require(threw); - - threw = false; - try { - WKOpenPanelResultListenerChooseFiles(nullptr, nullptr, nullptr); - } catch (const std::invalid_argument&) { - threw = true; - } - Require(threw); -} - -static void TestCertificateInfo() { - Require(WKCertificateInfoGetTypeID() == 0); - Require(WKCertificateInfoCreateWithCertficateChain(nullptr) == nullptr); - Require(WKCertificateInfoGetVerificationError(nullptr) == 0); - Require(WKCertificateInfoCopyVerificationErrorDescription(nullptr) == nullptr); - Require(WKCertificateInfoGetCertificateChainSize(nullptr) == 0); - Require(WKCertificateInfoCopyCertificateAtIndex(nullptr, 0) == nullptr); -} - -int main() { - TestTypeIDs(); - TestSecurityOrigin(); - TestRetainRelease(); - TestStrings(); - TestURL(); - TestArray(); - TestDictionary(); - TestData(); - TestNumbers(); - TestGeometry(); - TestUserContentURLPattern(); - TestURLRequestResponse(); - TestError(); TestOpenPanelResultListener(); TestCertificateInfo(); std::printf("All libSceNKWebKit Base tests passed!\n"); diff --git a/docs/dev/debt/libSceNKWebKit.md b/docs/dev/debt/libSceNKWebKit.md index 2c3044c7f9..27ff2910a4 100644 --- a/docs/dev/debt/libSceNKWebKit.md +++ b/docs/dev/debt/libSceNKWebKit.md @@ -18,7 +18,7 @@ Inventory prose retains historical counts; use the [generated progress report](h - [WTF language exports](../../../core/libs/prx/libSceNKWebKit/src/WtfLanguage.cpp) call language change observers in registration order; the console iterates a `HashMap` keyed by context in hash-table order. The 16-byte `Vector` layout and the rest of the behavior follow the [PS5 11.x WebKit source](https://github.com/neonmodder123/PS5-WebKit-11.x/blob/80f77e94c63ee7202eec3d4b5b1a021dd89011ba/Source/WTF/wtf/Language.cpp) and have no console verification. Null and `-1` contexts and null observers throw instead of reproducing undefined behavior. Closes when: compared with a console module. ### Functional -- [Additional callable exports](../../../core/libs/prx/libSceNKWebKit/Export.cpp) - 7367 of the 8703 inventory declarations listed below throw through `NotImplemented_nid_no_patch`; signatures and behavior are unverified. The later 13.60-labelled image contains all 8703; 8448 match the public 3.20 catalogue, while 255 are absent and their presence in retail 3.20 is unverified. Per-image records show the latter in a separate 3.20-labelled image, but neither image label is authenticated. Identified names match NIDs in the [public symbol database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv) or [supplemental catalog](https://github.com/claimore22/ps5rs/blob/2304be5131221bae0dac24b9767cabffda33f747/data/nids.csv). Data exports need separate analysis. +- [HitTestResult getters](../../../core/libs/prx/libSceNKWebKit/src/HitTestResult.cpp) (libSceNKWebKit) - WKHitTestResult* getters return image/link/media/PDF URLs, link text, bounding box and content editable flag from the host object. - [Constant-return exports](../../../core/libs/prx/libSceNKWebKit/src/ConstantExports.cpp) (libSceNKWebKit) - 137 exports answer with a fixed value in the PS5 11.00 module: 111 execute `xor eax,eax; ret`, 13 `mov eax,imm32; ret` and 13 `mov al,1; ret` (addresses and bodies from the 11.00 `libSceNKWebKit.sprx`, SHA-256 `3af992253665387feeb5df90ff27dff683e3aa270cc4d92a0535b46ab18fa20d`; NIDs recomputed from the declared names). The 124 EAX-writing bodies measure the 32-bit return; the 13 `mov al,1` bodies write only the low byte, so for them only AL is measured and the upper bits are unspecified. The declared prototypes stay the no-parameter `int` placeholders, so no other width, pointer or floating-point return is claimed. Only 11.00 was measured. - [Security origins](../../../core/libs/prx/libSceNKWebKit/src/URL.cpp) (libSceNKWebKit) - `WKSecurityOriginCopyDatabaseIdentifier` still throws pending `encodeForFileName` (#14858). `WKSecurityOriginCreateFromString` and `WKSecurityOriginCreateFromDatabaseIdentifier` still throw; URL parsing and default-port normalization (#13766) are not wired into origin creation. Value objects follow [the pinned WebKit source](https://github.com/neonmodder123/PS5-WebKit-11.x/tree/80f77e94c63ee7202eec3d4b5b1a021dd89011ba/Source); console execution and ABI are unverified. @@ -44,7 +44,6 @@ Inventory prose retains historical counts; use the [generated progress report](h - The [revision-pinned identity inventory](https://gist.github.com/kyon-eth/d44995f181e1818977bd38f0e6fc0f34/8afcb14b0707a09b6274ff7f7bcf33ef46cfe2e7) records the module, library, symbol type and NIDs for the declarations in [Export.cpp](../../../core/libs/prx/libSceNKWebKit/Export.cpp); it is a metadata summary rather than independent verification. For the 255 absent from the public catalogue, [per-image ELF records](https://gist.github.com/kyon-eth/147be8dd556e9cced2577ccbed548972/1a659ecfe819e724675991a574a1aebb5fbb48e5) include the raw qualified names, module and library identities, symbol type, values and sizes; the record file SHA-256 is `44bdc4d6073192a8f41b7be359b75364b3d41dbbd11ce649924b8cebdd4ffe51`. Image labels are not authenticated. - [Additional callable exports](../../../core/libs/prx/libSceNKWebKit/Export.cpp) - 7364 of the 8703 inventory declarations listed below throw through `NotImplemented_nid_no_patch`; signatures and behavior are unverified. The later 13.60-labelled image contains all 8703; 8448 match the public 3.20 catalogue, while 255 are absent and their presence in retail 3.20 is unverified. Per-image records show the latter in a separate 3.20-labelled image, but neither image label is authenticated. Identified names match NIDs in the [public symbol database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv) or [supplemental catalog](https://github.com/claimore22/ps5rs/blob/2304be5131221bae0dac24b9767cabffda33f747/data/nids.csv). Data exports need separate analysis. ->>>>>>> 4108e01b31 (feat(libSceNKWebKit): implement WKOpenPanelResultListener functions) | Name | NID | | --- | --- | @@ -7426,9 +7425,6 @@ Inventory prose retains historical counts; use the [generated progress report](h - [WTF::FileSystemImpl::encodeForFileName, decodeFromFilename and lastComponentOfPathIgnoringTrailingSlash](../../../core/libs/prx/libSceNKWebKit/src/WtfString.cpp) - are source-guided by pinned [FileSystem.cpp](https://github.com/WebKit/WebKit/blob/c55364d53f97/Source/WTF/wtf/FileSystem.cpp#L66-L210), not console-measured; the device's WebKit revision is unverified. A null `String` reference, or an encoded result above 2^31-1 units, throws where WebKit has undefined behaviour or a release assertion. - [WTF decimal parsing](../../../core/libs/prx/libSceNKWebKit/CharactersToNumber.cpp) - the device's WebKit revision is unverified; `charactersToDouble`, `charactersToFloat` and `StringToDoubleConverter` follow pinned WebKit [toDoubleType](https://github.com/WebKit/WebKit/blob/c55364d53f97/Source/WTF/wtf/text/WTFString.cpp#L1040-L1098) and [StringToIeee](https://github.com/WebKit/WebKit/blob/c55364d53f97/Source/WTF/wtf/dtoa/double-conversion.cc#L459-L664). Under a rounding mode other than round-to-nearest, WebKit's [exact-power fast path](https://github.com/WebKit/WebKit/blob/c55364d53f97/Source/WTF/wtf/dtoa/strtod.cc#L219-L254) and the double-to-float cast in [Strtof](https://github.com/WebKit/WebKit/blob/c55364d53f97/Source/WTF/wtf/dtoa/strtod.cc#L485-L588) round in the current mode and the host conversion may round differently. - [WTF decimal parsing limits](../../../core/libs/prx/libSceNKWebKit/CharactersToNumber.cpp) - inputs of about 1 GiB or more whose decimal exponent leaves the range of WebKit's [`int` exponent](https://github.com/WebKit/WebKit/blob/c55364d53f97/Source/WTF/wtf/dtoa/double-conversion.cc#L488-L490) throw, where WebKit overflows. A UTF-16 `charactersTo*` length above `UINT32_MAX` after leading spaces throws where WebKit's [conversion Vector](https://github.com/WebKit/WebKit/blob/c55364d53f97/Source/WTF/wtf/Vector.h#L291-L293) crashes. -<<<<<<< HEAD - [WTF StringBuilder](../../../core/libs/prx/libSceNKWebKit/src/StringBuilder.cpp) (libSceNKWebKit) - throws where WebKit crashes: an overflow of a builder that crashes on overflow, a length sum above `UINT32_MAX`, `toString` after an overflow, and null characters with a nonzero length. It also throws where a debug assertion guards memory WebKit would corrupt: `reserveCapacity` on an empty 16-bit builder without a buffer, and [`appendQuotedJSONString`](https://github.com/WebKit/WebKit/blob/c55364d53f97/Source/WTF/wtf/text/StringBuilderJSON.cpp#L127-L128) of a 16-bit string into an 8-bit builder that holds an adopted string and no buffer. A capacity above the tree's `StringImpl` limit records an overflow, as a failed allocation does. Growing or shrinking a buffer with one reference copies it instead of calling `fastRealloc`, so its address always changes. - [WTF TextStream pointers](../../../core/libs/prx/libSceNKWebKit/src/TextStream.cpp) (libSceNKWebKit) - `operator<<(const void*)` formats the pointer as the tree's libc formats `%p` ([`snprintf`](https://github.com/WebKit/WebKit/blob/c55364d53f97/Source/WTF/wtf/text/TextStream.cpp#L114-L119) in WebKit); the console libc's output is unverified. -======= - [WKOpenPanelResultListener functions](../../../core/libs/prx/libSceNKWebKit/src/OpenPanelResultListener.cpp) (libSceNKWebKit) - nothing in the library creates a WKOpenPanelResultListener (no UI client requests open panels); stored results are not consumed by a file picker dialog; upstream invalidates the listener after the first call, and a null file array is treated as empty. ->>>>>>> 3e08de0518 (feat(libSceNKWebKit): implement WKOpenPanelResultListener functions)