feat(libSceLibreSsl): implement historical error strings and newest peeking

This commit is contained in:
Felipebmaitan committed 2026-10-21 21:31:08 -03:00
1 parent ca32e706e8
commit 562e68e6ae
16 files changed
+434 -43

No files matched your search

+3
View File
@@ -1,6 +1,9 @@
[submodule "3rdparty/SDL2"]
path = 3rdparty/SDL2
url = https://github.com/libsdl-org/SDL.git
[submodule "3rdparty/libressl-openbsd"]
path = 3rdparty/libressl-openbsd
url = https://github.com/libressl/openbsd.git
[submodule "3rdparty/Vulkan-Headers"]
path = 3rdparty/Vulkan-Headers
url = https://github.com/KhronosGroup/Vulkan-Headers.git
Vendored Submodule
+1
+1
View File
@@ -186,6 +186,7 @@ foreach(LIB_DIR ${LIBS_DIRS})
endif()
endforeach()
include(prx/libSceLibreSsl/ErrorQueue.cmake)
target_link_libraries(libSceUsbStorageDialog PRIVATE libSceUserService)
if(APS5_ENABLE_TRACY)
@@ -1,16 +0,0 @@
add_library(libSceLibreSsl SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Error.cpp)
target_include_directories(libSceLibreSsl PRIVATE ${LIBS_INCLUDE_DIR})
configure_windows_unwind(libSceLibreSsl)
if(BUILD_TESTING)
add_test_executable(guest_libressl_error_tests ${LIBS_INCLUDE_DIR}/tests/GuestLibreSslError.cpp)
target_include_directories(guest_libressl_error_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_libressl_error_tests PRIVATE libSceLibreSsl libc)
configure_windows_unwind(guest_libressl_error_tests)
add_test(NAME guest_libressl_error COMMAND guest_libressl_error_tests)
set_tests_properties(guest_libressl_error PROPERTIES TIMEOUT 30)
if(WIN32)
set_tests_properties(guest_libressl_error PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceLibreSsl>")
endif()
endif()
@@ -0,0 +1,37 @@
find_package(Python3 REQUIRED COMPONENTS Interpreter)
set(libreSslSource ${PROJECT_SOURCE_DIR}/3rdparty/libressl-openbsd/src/lib/libcrypto)
if(NOT EXISTS ${libreSslSource}/err/err.c)
message(FATAL_ERROR "Initialize 3rdparty/libressl-openbsd with git submodule update --init")
endif()
file(GLOB_RECURSE libreSslStringSources CONFIGURE_DEPENDS ${libreSslSource}/*.h ${libreSslSource}/*.c)
set(libreSslStrings ${CMAKE_CURRENT_BINARY_DIR}/prx/libSceLibreSsl/ErrorStrings.inc)
add_custom_command(OUTPUT ${libreSslStrings}
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/GenerateErrorStrings.py ${libreSslSource} ${libreSslStrings}
DEPENDS ${CMAKE_CURRENT_LIST_DIR}/GenerateErrorStrings.py ${libreSslStringSources}
VERBATIM)
target_sources(libSceLibreSsl PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src/Error.cpp
${CMAKE_CURRENT_LIST_DIR}/ErrorQueue.cpp ${libreSslStrings})
target_include_directories(libSceLibreSsl PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/prx/libSceLibreSsl)
target_link_libraries(libSceLibreSsl3 PRIVATE libSceLibreSsl)
if(BUILD_TESTING)
add_test_executable(guest_libressl_error_tests ${LIBS_INCLUDE_DIR}/tests/GuestLibreSslError.cpp)
target_include_directories(guest_libressl_error_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_libressl_error_tests PRIVATE libSceLibreSsl libSceLibreSsl3)
configure_windows_unwind(guest_libressl_error_tests)
add_test(NAME guest_libressl_error COMMAND guest_libressl_error_tests)
add_test_executable(guest_libressl_error_string_tests ${LIBS_INCLUDE_DIR}/tests/GuestLibreSslErrors.cpp)
target_include_directories(guest_libressl_error_string_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_libressl_error_string_tests PRIVATE libSceLibreSsl libSceLibreSsl3)
configure_windows_unwind(guest_libressl_error_string_tests)
add_test(NAME guest_libressl_errors COMMAND guest_libressl_error_string_tests)
add_test(NAME guest_libressl_free_strings COMMAND guest_libressl_error_string_tests free)
add_test(NAME guest_libressl_free_strings_first COMMAND guest_libressl_error_string_tests free-first)
set_tests_properties(guest_libressl_error guest_libressl_errors guest_libressl_free_strings
guest_libressl_free_strings_first PROPERTIES TIMEOUT 15)
if(WIN32)
set_tests_properties(guest_libressl_error guest_libressl_errors guest_libressl_free_strings
guest_libressl_free_strings_first PROPERTIES
ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceLibreSsl>")
endif()
endif()
+102
View File
@@ -0,0 +1,102 @@
#include "ErrorQueue.hpp"
#include "prx/libc/include/General.hpp"
#include <algorithm>
#include <atomic>
#include <cstdio>
#include <cstring>
#include <iterator>
extern "C" int APS5_VABI strerror_r_nid_postfix(int error, char* buffer, std::size_t length);
namespace {
struct ErrorString {
std::uint32_t code;
const char* text;
};
#include "ErrorStrings.inc"
enum class StringState { Uninitialized, Loaded, Freed };
std::atomic<StringState> stringState{StringState::Uninitialized};
char errorBuffer[256];
constexpr std::uint64_t Pack(int library, int function, int reason) {
return ((static_cast<std::uint64_t>(library) & 255) << 24) |
((static_cast<std::uint64_t>(function) & 4095) << 12) |
(static_cast<std::uint64_t>(reason) & 4095);
}
const char* Lookup(std::uint32_t code) {
const auto entry = std::lower_bound(std::begin(CryptoStrings), std::end(CryptoStrings), code,
[](const ErrorString& value, std::uint32_t key) { return value.code < key; });
return entry != std::end(CryptoStrings) && entry->code == code ? entry->text : nullptr;
}
}
extern "C" {
void LibreSslLoadErrorStrings_nid_no_patch() {
auto expected = StringState::Uninitialized;
stringState.compare_exchange_strong(expected, StringState::Loaded);
}
char* APS5_VABI ERR_error_string_nid_postfix(std::uint64_t error, char* buffer) {
if (buffer == nullptr) buffer = errorBuffer;
ERR_error_string_n_nid_postfix(error, buffer, sizeof(errorBuffer));
return buffer;
}
void APS5_VABI ERR_error_string_n_nid_postfix(std::uint64_t error, char* buffer, std::size_t length) {
if (buffer == nullptr && length != 0) NotImplemented_nid_no_patch(__func__);
const int library = (error >> 24) & 255;
const int function = (error >> 12) & 4095;
const int reason = error & 4095;
LibreSslLoadErrorStrings_nid_no_patch();
const bool loaded = stringState.load() == StringState::Loaded;
const char* libraryText = loaded ? Lookup(static_cast<std::uint32_t>(Pack(library, 0, 0))) : nullptr;
const char* functionText = loaded ? Lookup(static_cast<std::uint32_t>(Pack(library, function, 0))) : nullptr;
const char* reasonText = loaded ? Lookup(static_cast<std::uint32_t>(Pack(library, 0, reason))) : nullptr;
char libraryBuffer[30];
char functionBuffer[30];
char reasonBuffer[32];
if (loaded && library == 2 && reason >= 1 && reason <= 127) {
char message[128];
strerror_r_nid_postfix(reason, message, sizeof(message));
std::memcpy(reasonBuffer, message, std::min(std::strlen(message) + 1, sizeof(reasonBuffer)));
reasonBuffer[sizeof(reasonBuffer) - 1] = '\0';
reasonText = reasonBuffer;
}
if (reasonText == nullptr && loaded) reasonText = Lookup(reason);
if (libraryText == nullptr) {
std::snprintf(libraryBuffer, sizeof(libraryBuffer), "lib(%d)", library);
libraryText = libraryBuffer;
}
if (functionText == nullptr) {
std::snprintf(functionBuffer, sizeof(functionBuffer), "func(%d)", function);
functionText = functionBuffer;
}
if (reasonText == nullptr) {
std::snprintf(reasonBuffer, sizeof(reasonBuffer), "reason(%d)", reason);
reasonText = reasonBuffer;
}
const int written = std::snprintf(buffer, length, "error:%08llX:%s:%s:%s",
static_cast<unsigned long long>(error), libraryText, functionText, reasonText);
if (written >= 0 && static_cast<std::size_t>(written) >= length && length > 4) {
char* next = buffer;
for (std::size_t index = 0; index < 4; ++index) {
char* colon = std::strchr(next, ':');
char* last = buffer + length - 5 + index;
if (colon == nullptr || colon > last) {
colon = last;
*colon = ':';
}
next = colon + 1;
}
}
}
void APS5_VABI ERR_free_strings_nid_postfix() {
LibreSslLoadErrorStrings_nid_no_patch();
stringState.store(StringState::Freed);
}
}
@@ -0,0 +1,22 @@
#ifndef CORE_LIBS_PRX_LIBSCELIBRESSL_ERRORQUEUE_HPP
#define CORE_LIBS_PRX_LIBSCELIBRESSL_ERRORQUEUE_HPP
#include <cstddef>
#include <cstdint>
#include "include/Error.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
extern "C" {
std::uint64_t LibreSslPeekLastError_nid_no_patch();
void LibreSslLoadErrorStrings_nid_no_patch();
void APS5_VABI ERR_clear_error_nid_postfix();
char* APS5_VABI ERR_error_string_nid_postfix(std::uint64_t error, char* buffer);
void APS5_VABI ERR_error_string_n_nid_postfix(std::uint64_t error, char* buffer, std::size_t length);
void APS5_VABI ERR_free_strings_nid_postfix();
std::uint64_t APS5_VABI ERR_get_error_nid_postfix();
std::uint64_t APS5_VABI ERR_peek_error_nid_postfix();
void APS5_VABI ERR_remove_thread_state_nid_postfix(const void* id);
}
#endif
-15
View File
@@ -506,21 +506,6 @@ int APS5_VABI ENGINE_set_default_nid_postfix(void) {
return 0;
}
int APS5_VABI ERR_error_string_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI ERR_error_string_n_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI ERR_free_strings_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI EVP_CIPHER_CTX_cleanup_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
@@ -0,0 +1,97 @@
import ast
import json
import operator
from pathlib import Path
import re
import sys
source = Path(sys.argv[1])
destination = Path(sys.argv[2])
macros = {}
operations = {ast.BitOr: operator.or_, ast.BitAnd: operator.and_, ast.LShift: operator.lshift,
ast.RShift: operator.rshift, ast.Add: operator.add, ast.Sub: operator.sub}
def uncomment(text):
return re.sub(r"/\*.*?\*/", "", text, flags=re.S)
for header in sorted(source.rglob("*.h")):
for name, value in re.findall(r"^#\s*define\s+(\w+)\s+([^\n]+)", uncomment(header.read_text(encoding="utf-8")), re.M):
if re.fullmatch(r"ERR_(?:LIB|R)_\w+|\w+_[RF]_\w+", name):
if name in macros and macros[name] != value.strip():
raise ValueError(f"Conflicting definition: {name}")
macros[name] = value.strip()
def evaluate(expression):
def visit(node):
if isinstance(node, ast.Constant) and type(node.value) is int:
return node.value
if isinstance(node, ast.Name):
return evaluate(macros[node.id])
if isinstance(node, ast.BinOp) and type(node.op) in operations:
return operations[type(node.op)](visit(node.left), visit(node.right))
raise ValueError(f"Unsupported constant: {expression}")
return visit(ast.parse(expression, mode="eval").body)
def pack(library, function, reason):
return ((library & 255) << 24) | ((function & 4095) << 12) | (reason & 4095)
def code(expression, library):
call = re.fullmatch(r"ERR_(PACK|FUNC|REASON)\((.*)\)", expression.strip())
if call:
values = [evaluate(value.strip()) for value in call[2].split(",")]
if call[1] == "PACK":
return pack(*values)
if call[1] == "FUNC":
return pack(library, values[0], 0)
return pack(library, 0, values[0])
return evaluate(expression.strip())
loader = uncomment((source / "err/err_all.c").read_text(encoding="utf-8"))
loaded = set(re.findall(r"\bERR_load_(\w+)_strings\(\);", loader)) - {"ERR"}
strings = {}
licenses = []
found = set()
for path in sorted(source.rglob("*.c")):
text = path.read_text(encoding="latin-1")
clean = uncomment(text)
library = 0
if path != source / "err/err.c":
names = set(re.findall(r"\bERR_load_(\w+)_strings\(void\)", clean)) & loaded
if not names:
continue
found.update(names)
library_name = re.search(r"#define ERR_FUNC\(func\)\s+ERR_PACK\((\w+),", clean)
if not library_name:
raise ValueError(f"Missing library: {path}")
library = evaluate(library_name[1])
licenses.append(text[:text.index("#include")])
tables = re.findall(r"static ERR_STRING_DATA (\w+)\[\]\s*=\s*\{(.*?)\n\};", clean, re.S)
if not tables:
raise ValueError(f"Missing string tables: {path}")
for name, body in tables:
entries = re.findall(r'\{\s*([^{}]+?),\s*("(?:[^"\\]|\\.)*"|NULL)\s*\}', body, re.S)
remainder = re.sub(r'\{\s*([^{}]+?),\s*("(?:[^"\\]|\\.)*"|NULL)\s*\}', "", body, flags=re.S)
if remainder.strip(" \t\r\n,"):
raise ValueError(f"Unsupported table: {path}: {name}")
for expression, message in entries:
value = code(expression, library)
if value == 0 and message == "NULL":
continue
if message == "NULL":
raise ValueError(f"Missing message: {path}: {expression}")
if name == "ERR_str_functs":
value |= pack(evaluate("ERR_LIB_SYS"), 0, 0)
strings[value] = json.loads(message)
if found != loaded:
raise ValueError(f"Missing libraries: {loaded - found}")
destination.parent.mkdir(parents=True, exist_ok=True)
destination.write_text("\n".join(licenses) + "\nconstexpr ErrorString CryptoStrings[] = {\n" +
"".join(f" {{0x{value:08X}, {json.dumps(message)}}},\n"
for value, message in sorted(strings.items())) + "};\n", encoding="utf-8")
@@ -3,7 +3,7 @@
extern "C" {
void LibreSslPutError_nid_no_patch(int library, int function, int reason);
void LibreSslPutError_nid_no_patch(int library, int function, int reason, const char* file = nullptr, int line = 0);
}
+15 -2
View File
@@ -1,4 +1,5 @@
#include <array>
#include <cerrno>
#include <cstdint>
#include <type_traits>
#include "prx/libSceLibreSsl/include/Error.hpp"
@@ -9,6 +10,8 @@ constexpr int QueueSize = 16;
struct ErrorQueue {
std::array<std::uint64_t, QueueSize> codes;
std::array<const char*, QueueSize> files;
std::array<int, QueueSize> lines;
int top;
int bottom;
};
@@ -24,6 +27,8 @@ std::uint64_t Oldest(bool remove) {
if (remove) {
queue.bottom = index;
queue.codes[index] = 0;
queue.files[index] = nullptr;
queue.lines[index] = 0;
}
return code;
}
@@ -35,12 +40,20 @@ void Clear() {
extern "C" {
void LibreSslPutError_nid_no_patch(int library, int function, int reason) {
void LibreSslPutError_nid_no_patch(int library, int function, int reason, const char* file, int line) {
const int savedError = errno;
queue.top = (queue.top + 1) % QueueSize;
if (queue.top == queue.bottom) queue.bottom = (queue.bottom + 1) % QueueSize;
queue.codes[queue.top] = (static_cast<std::uint64_t>(library) & 0xff) << 24 |
(static_cast<std::uint64_t>(function) & 0xfff) << 12 |
(static_cast<std::uint64_t>(reason) & 0xfff);
queue.files[queue.top] = file;
queue.lines[queue.top] = line;
errno = savedError;
}
std::uint64_t LibreSslPeekLastError_nid_no_patch() {
return queue.top == queue.bottom ? 0 : queue.codes[queue.top];
}
std::uint64_t APS5_VABI ERR_get_error_nid_postfix() {
@@ -56,7 +69,7 @@ void APS5_VABI ERR_clear_error_nid_postfix() {
}
void APS5_VABI ERR_remove_thread_state_nid_postfix(const void* threadId) {
if (threadId != nullptr) NotImplemented_nid_no_patch("ERR_remove_thread_state of a CRYPTO_THREADID");
if (threadId != nullptr) NotImplemented_nid_no_patch(__func__);
Clear();
}
+5 -6
View File
@@ -1,6 +1,7 @@
#include <cstddef>
#include <cstdint>
#include "SceTypes.hpp"
#include "prx/libSceLibreSsl/ErrorQueue.hpp"
#include "prx/libc/include/General.hpp"
extern "C" {
@@ -61,14 +62,12 @@ int APS5_VABI EC_KEY_up_ref_nid_postfix(void) {
return 0;
}
int APS5_VABI ERR_load_crypto_strings_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
void APS5_VABI ERR_load_crypto_strings_nid_postfix() {
LibreSslLoadErrorStrings_nid_no_patch();
}
int APS5_VABI ERR_peek_last_error_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
std::uint64_t APS5_VABI ERR_peek_last_error_nid_postfix() {
return LibreSslPeekLastError_nid_no_patch();
}
int APS5_VABI ERR_print_errors_cb_nid_postfix(void) {
+1 -1
View File
@@ -1,4 +1,5 @@
#include "prx/libc/include/general/VabiMacros.hpp"
#include "prx/libSceLibreSsl/include/Error.hpp"
#include <cstdint>
#include <cstdio>
#include <cstdlib>
@@ -10,7 +11,6 @@ std::uint64_t APS5_VABI ERR_get_error_nid_postfix();
std::uint64_t APS5_VABI ERR_peek_error_nid_postfix();
void APS5_VABI ERR_clear_error_nid_postfix();
void APS5_VABI ERR_remove_thread_state_nid_postfix(const void*);
void LibreSslPutError_nid_no_patch(int library, int function, int reason);
}
static void Require(bool value, const char* what) {
+139
View File
@@ -0,0 +1,139 @@
#include "prx/libSceLibreSsl/ErrorQueue.hpp"
#include <array>
#include <atomic>
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <thread>
extern "C" {
void APS5_VABI ERR_load_crypto_strings_nid_postfix();
std::uint64_t APS5_VABI ERR_peek_last_error_nid_postfix();
int* APS5_VABI __error_nid_postfix();
}
static void Require(bool value, const char* message) {
if (value) return;
std::fputs(message, stderr);
std::fputc('\n', stderr);
std::abort();
}
static void ExpectString(std::uint64_t code, const char* expected) {
std::array<char, 256> buffer{};
Require(ERR_error_string_nid_postfix(code, buffer.data()) == buffer.data(), "caller buffer returned");
Require(std::strcmp(buffer.data(), expected) == 0, buffer.data());
}
static void TestStrings() {
ERR_load_crypto_strings_nid_postfix();
ExpectString(0, "error:00000000:lib(0):func(0):reason(0)");
ExpectString(0x20fff043, "error:20FFF043:BIO routines:CRYPTO_internal:passed a null parameter");
ExpectString(0x20fff07e, "error:20FFF07E:BIO routines:CRYPTO_internal:write to read only BIO");
ExpectString(0x03fff06c, "error:03FFF06C:bignum routines:CRYPTO_internal:no inverse");
ExpectString(0xe1abc789, "error:E1ABC789:lib(225):func(2748):reason(1929)");
ExpectString(0x12345678e1abc789, "error:12345678E1ABC789:lib(225):func(2748):reason(1929)");
ExpectString(0x02002023, "error:02002023:system library:connect:Resource temporarily unavailabl");
auto* shared = ERR_error_string_nid_postfix(0xe1abc789, nullptr);
Require(shared != nullptr, "static buffer exists");
Require(ERR_error_string_nid_postfix(0, nullptr) == shared, "static buffer reused");
Require(std::strcmp(shared, "error:00000000:lib(0):func(0):reason(0)") == 0, "static buffer overwritten");
constexpr auto expected = "error:E1ABC789:lib(225):func(2748):reason(1929)";
for (std::size_t size = 0; size <= std::strlen(expected) + 2; ++size) {
std::array<char, 260> buffer;
buffer.fill('#');
ERR_error_string_n_nid_postfix(0xe1abc789, buffer.data() + 1, size);
Require(buffer.front() == '#' && buffer[size + 1] == '#', "bounded formatting canaries");
if (size == 0) continue;
const auto count = std::strlen(buffer.data() + 1);
Require(count < size, "bounded formatting terminates");
if (size > 4 && size <= std::strlen(expected)) {
std::size_t colons = 0;
for (std::size_t index = 1; index <= count; ++index) colons += buffer[index] == ':';
Require(colons == 4, "truncation keeps four colons");
}
if (size > std::strlen(expected)) Require(std::strcmp(buffer.data() + 1, expected) == 0, "full bounded string");
}
char tiny[6]{};
ERR_error_string_n_nid_postfix(0xe1abc789, tiny, sizeof(tiny));
Require(std::strcmp(tiny, "e::::") == 0, "historical truncation placement");
ERR_error_string_n_nid_postfix(0, nullptr, 0);
}
static void TestQueue() {
ERR_clear_error_nid_postfix();
Require(ERR_get_error_nid_postfix() == 0 && ERR_peek_error_nid_postfix() == 0 &&
ERR_peek_last_error_nid_postfix() == 0, "initially empty");
auto* guestError = __error_nid_postfix();
errno = EDOM;
const int savedGuestError = *guestError;
LibreSslPutError_nid_no_patch(32, 0xfff, 67, "producer", 19);
Require(errno == EDOM, "publication preserves host errno");
Require(*guestError == savedGuestError, "publication preserves guest errno");
LibreSslPutError_nid_no_patch(3, 0xfff, 108, nullptr, 0);
Require(ERR_peek_error_nid_postfix() == 0x20fff043 && ERR_peek_error_nid_postfix() == 0x20fff043, "peek oldest without removal");
Require(ERR_peek_last_error_nid_postfix() == 0x03fff06c && ERR_peek_last_error_nid_postfix() == 0x03fff06c, "cross-library peek newest");
Require(ERR_get_error_nid_postfix() == 0x20fff043 && ERR_get_error_nid_postfix() == 0x03fff06c, "FIFO removal");
Require(ERR_get_error_nid_postfix() == 0 && ERR_peek_last_error_nid_postfix() == 0, "drained queue");
LibreSslPutError_nid_no_patch(-1, -1, -1, nullptr, 0);
Require(ERR_get_error_nid_postfix() == 0xffffffff, "pack masks negative fields");
LibreSslPutError_nid_no_patch(0x132, 0x2345, 0x3456, nullptr, 0);
Require(ERR_get_error_nid_postfix() == 0x32345456, "pack masks oversized fields");
LibreSslPutError_nid_no_patch(0, 0, 0, nullptr, 0);
LibreSslPutError_nid_no_patch(0, 0, 1, nullptr, 0);
Require(ERR_get_error_nid_postfix() == 0 && ERR_get_error_nid_postfix() == 1, "zero code occupies a slot");
for (int index = 1; index <= 100; ++index) LibreSslPutError_nid_no_patch(128, index, index, nullptr, index);
for (std::uint64_t index = 86; index <= 100; ++index) {
Require(ERR_get_error_nid_postfix() == (0x80000000 | (index << 12) | index), "retain newest fifteen after overflow");
}
Require(ERR_get_error_nid_postfix() == 0, "overflow queue drained");
LibreSslPutError_nid_no_patch(3, 1, 2, nullptr, 0);
ERR_clear_error_nid_postfix();
Require(ERR_peek_error_nid_postfix() == 0 && ERR_peek_last_error_nid_postfix() == 0, "clear both queue ends");
LibreSslPutError_nid_no_patch(32, 0xfff, 67, nullptr, 0);
ERR_remove_thread_state_nid_postfix(nullptr);
Require(ERR_get_error_nid_postfix() == 0, "remove current state");
LibreSslPutError_nid_no_patch(32, 0xfff, 67, nullptr, 0);
Require(ERR_get_error_nid_postfix() == 0x20fff043, "reuse after remove");
}
static void TestThreads() {
ERR_clear_error_nid_postfix();
LibreSslPutError_nid_no_patch(128, 7, 19, nullptr, 0);
std::atomic<int> ready{0};
std::array<std::thread, 8> threads;
for (std::size_t index = 0; index < threads.size(); ++index) {
threads[index] = std::thread([&, index] {
Require(ERR_get_error_nid_postfix() == 0, "new thread starts empty");
LibreSslPutError_nid_no_patch(129, 0, static_cast<int>(index + 1), nullptr, 0);
++ready;
while (ready.load() != static_cast<int>(threads.size())) std::this_thread::yield();
Require(ERR_peek_last_error_nid_postfix() == 0x81000001 + index, "thread-local newest");
if (index % 2) ERR_clear_error_nid_postfix();
else ERR_remove_thread_state_nid_postfix(nullptr);
Require(ERR_get_error_nid_postfix() == 0, "thread-local cleanup");
LibreSslPutError_nid_no_patch(130, 1, 3, nullptr, 0);
});
}
for (auto& thread : threads) thread.join();
Require(ERR_get_error_nid_postfix() == 0x80007013, "child queues leave parent unchanged");
std::thread replacement([] { Require(ERR_get_error_nid_postfix() == 0, "no stale state after thread exit"); });
replacement.join();
}
int main(int argc, char** argv) {
if (argc == 2) {
if (std::strcmp(argv[1], "free") == 0) TestStrings();
LibreSslPutError_nid_no_patch(32, 0xfff, 67, nullptr, 0);
ERR_free_strings_nid_postfix();
ERR_free_strings_nid_postfix();
ERR_load_crypto_strings_nid_postfix();
ExpectString(0x20fff043, "error:20FFF043:lib(32):func(4095):reason(67)");
Require(ERR_get_error_nid_postfix() == 0x20fff043, "free strings preserves queue");
return 0;
}
TestStrings();
TestQueue();
TestThreads();
}
+6 -2
View File
@@ -1,6 +1,10 @@
# libSceLibreSsl technical debt
### Functional
- [ERR queues and strings](../../../core/libs/prx/libSceLibreSsl/ErrorQueue.cpp) have no PS5 equivalence verification. Contracts follow LibreSSL 3.4.1's [err.c](https://github.com/libressl/openbsd/blob/078b3d1df8ca2011c1605cd40418c58fa0f5b143/src/lib/libcrypto/err/err.c), err.h, crypto.h and err_all.c at that revision: LP64 error arguments/results, 15 queued errors and historical crypto strings. System reasons use guest libc's FreeBSD errno messages, truncated to 31 bytes; console wording is unverified. Explicit thread IDs in `ERR_remove_thread_state` throw because guest pthread identity mapping is unverified. Null IDs clear the calling thread. SSL-specific strings are not loaded; SSL initialization remains unimplemented.
- [Error publication](../../../core/libs/prx/libSceLibreSsl/ErrorQueue.hpp) has no implemented production callers on this branch. BIOs and the digest, AES, allocator/RAND, DES, BIGNUM and EVP/HMAC work in pending branches require separate integration. `LibreSslPutError_nid_no_patch(int library, int function, int reason, const char* file, int line)` has C linkage and the native host calling convention, accepts upstream numeric fields and publishes to the calling thread's queue. Three-argument source calls default file/line to null/zero. It stores file/line without taking ownership; producers must supply a filename with static lifetime. Guest ERR entry points use `APS5_VABI`. Unsupported producer exports continue throwing; the local tests publish through this helper.
### Unknown function info
- [libSceLibreSsl pending exports](../../../core/libs/prx/libSceLibreSsl/Export.cpp) - 395 named callable exports are listed in the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceLibreSsl.c) and were cross-checked against the same module in PS5 firmware 13.60. Their signatures and behaviour are unverified; the declarations use the no-parameter unknown-signature convention and throw through `NotImplemented_nid_no_patch`.
- [ERR_get_error, ERR_peek_error, ERR_clear_error, ERR_remove_thread_state](../../../core/libs/prx/libSceLibreSsl/src/Error.cpp) (libSceLibreSsl) - no export of this library adds an error yet, so the queue stays empty for guest code. The per-thread queue of 16 slots holding 15 codes, its order and the `ERR_PACK` layout follow LibreSSL's [err.c](https://github.com/libressl/openbsd/blob/bc9483725c5815efb64b6d9acc52fc5fba139212/src/lib/libcrypto/err/err.c) and [err.h](https://github.com/libressl/openbsd/blob/bc9483725c5815efb64b6d9acc52fc5fba139212/src/lib/libcrypto/err/err.h); the module's LibreSSL version is unknown and nothing is checked on the console. The queue keeps no file, line or data, as no export reads them. `ERR_remove_thread_state` with a thread id throws: that version asserts a null id, while older versions remove the state of the thread the id names
- [libSceLibreSsl pending exports](../../../core/libs/prx/libSceLibreSsl/Export.cpp) - 399 named callable exports are listed in the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceLibreSsl.c) and were cross-checked against the same module in PS5 firmware 13.60. Their signatures and behaviour are unverified; the declarations use the no-parameter unknown-signature convention and throw through `NotImplemented_nid_no_patch`.
+4
View File
@@ -1,5 +1,9 @@
# libSceLibreSsl3 technical debt
### Functional
- [ERR_load_crypto_strings and ERR_peek_last_error](../../../core/libs/prx/libSceLibreSsl3/Export.cpp) share libSceLibreSsl's string registry and calling-thread queue. This relationship and PS5 equivalence are unverified; the public contracts follow LibreSSL 3.4.1 at OpenBSD commit `078b3d1df8ca2011c1605cd40418c58fa0f5b143`. `ERR_print_errors_cb` remains throwing: the public callback contract is known, but PS5 thread identifiers and diagnostic metadata have not been established.
### Unknown function info
- [libSceLibreSsl3 pending exports](../../../core/libs/prx/libSceLibreSsl3/Export.cpp) - 87 named callable exports are listed in the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceLibreSsl3.c) and were cross-checked against the same module in PS5 firmware 13.60. Their signatures and behaviour are unverified; the declarations use the no-parameter unknown-signature convention and throw through `NotImplemented_nid_no_patch`.