fix(libSceHttp2): validate SSL setter handles
This commit is contained in:
parent
a11c8f7f70
commit
4337b43f54
|
|
@ -6,19 +6,54 @@
|
|||
#include "prx/libkernel/Equeue/Equeue.hpp"
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
// No network is emulated: contexts, templates and requests can be created, but any request
|
||||
// that would touch the network fails with the library's network error.
|
||||
static constexpr int ERROR_NETWORK = static_cast<int>(0x80436063);
|
||||
static constexpr int ERROR_INVALID_ID = static_cast<int>(0x817B1100);
|
||||
static constexpr int ERROR_NULL_POINTER = static_cast<int>(0x817B1225);
|
||||
|
||||
enum class Http2Kind : std::uint8_t { Context, Template, Request, CookieBox };
|
||||
|
||||
struct Http2Handle {
|
||||
Http2Kind kind;
|
||||
int owner;
|
||||
std::size_t poolSize;
|
||||
};
|
||||
|
||||
static std::atomic<int> g_nextHandle{1};
|
||||
static std::mutex g_poolsMutex;
|
||||
static std::map<int, size_t> g_pools;
|
||||
static std::mutex g_handlesMutex;
|
||||
static std::unordered_map<int, Http2Handle> g_handles;
|
||||
|
||||
static Http2Handle* FindHandle(int id, Http2Kind kind) {
|
||||
const auto entry = g_handles.find(id);
|
||||
return entry != g_handles.end() && entry->second.kind == kind ? &entry->second : nullptr;
|
||||
}
|
||||
|
||||
static Http2Handle* FindAnyHandle(int id) {
|
||||
const auto entry = g_handles.find(id);
|
||||
return entry != g_handles.end() ? &entry->second : nullptr;
|
||||
}
|
||||
|
||||
static void ReleaseOwnedHandles(int owner) {
|
||||
std::vector<int> owned;
|
||||
for (const auto& entry : g_handles) {
|
||||
if (entry.second.owner == owner) owned.push_back(entry.first);
|
||||
}
|
||||
for (const int id : owned) {
|
||||
ReleaseOwnedHandles(id);
|
||||
g_handles.erase(id);
|
||||
}
|
||||
}
|
||||
|
||||
static bool HandleIsLive(int id) {
|
||||
std::lock_guard lock(g_handlesMutex);
|
||||
return FindAnyHandle(id) != nullptr;
|
||||
}
|
||||
|
||||
struct Http2MemoryPoolStats {
|
||||
size_t pool_size;
|
||||
|
|
@ -58,29 +93,43 @@ int APS5_VABI sceHttp2AddRequestHeader(int id, const char* name, const char* val
|
|||
}
|
||||
|
||||
int APS5_VABI sceHttp2CreateRequestWithURL(int tmpl_id, const char* method, const char* url, uint64_t content_length) {
|
||||
(void)tmpl_id;
|
||||
(void)method;
|
||||
(void)url;
|
||||
(void)content_length;
|
||||
return g_nextHandle.fetch_add(1, std::memory_order_relaxed);
|
||||
std::lock_guard lock(g_handlesMutex);
|
||||
if (FindHandle(tmpl_id, Http2Kind::Template) == nullptr) return ERROR_INVALID_ID;
|
||||
const int id = g_nextHandle.fetch_add(1, std::memory_order_relaxed);
|
||||
g_handles[id] = {Http2Kind::Request, tmpl_id, 0};
|
||||
return id;
|
||||
}
|
||||
|
||||
int APS5_VABI sceHttp2CreateTemplate(int lib_http2_ctx_id, const char* user_agent, int http_ver, int is_auto_proxy_conf) {
|
||||
(void)lib_http2_ctx_id;
|
||||
(void)user_agent;
|
||||
(void)http_ver;
|
||||
(void)is_auto_proxy_conf;
|
||||
return g_nextHandle.fetch_add(1, std::memory_order_relaxed);
|
||||
std::lock_guard lock(g_handlesMutex);
|
||||
if (FindHandle(lib_http2_ctx_id, Http2Kind::Context) == nullptr) return ERROR_INVALID_ID;
|
||||
const int id = g_nextHandle.fetch_add(1, std::memory_order_relaxed);
|
||||
g_handles[id] = {Http2Kind::Template, lib_http2_ctx_id, 0};
|
||||
return id;
|
||||
}
|
||||
|
||||
int APS5_VABI sceHttp2DeleteRequest(int req_id) {
|
||||
{
|
||||
std::lock_guard lock(g_handlesMutex);
|
||||
if (FindHandle(req_id, Http2Kind::Request) == nullptr) return ERROR_INVALID_ID;
|
||||
g_handles.erase(req_id);
|
||||
}
|
||||
std::lock_guard lock(g_completionsMutex);
|
||||
g_completions.erase(req_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceHttp2DeleteTemplate(int tmpl_id) {
|
||||
(void)tmpl_id;
|
||||
std::lock_guard lock(g_handlesMutex);
|
||||
if (FindHandle(tmpl_id, Http2Kind::Template) == nullptr) return ERROR_INVALID_ID;
|
||||
ReleaseOwnedHandles(tmpl_id);
|
||||
g_handles.erase(tmpl_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -109,8 +158,8 @@ int APS5_VABI sceHttp2Init(int libnet_mem_id, int libssl_ctx_id, size_t pool_siz
|
|||
(void)libssl_ctx_id;
|
||||
(void)max_concurrent_request;
|
||||
const int id = g_nextHandle.fetch_add(1, std::memory_order_relaxed);
|
||||
std::lock_guard lock(g_poolsMutex);
|
||||
g_pools[id] = pool_size;
|
||||
std::lock_guard lock(g_handlesMutex);
|
||||
g_handles[id] = {Http2Kind::Context, 0, pool_size};
|
||||
return id;
|
||||
}
|
||||
|
||||
|
|
@ -219,26 +268,25 @@ int APS5_VABI sceHttp2SetTimeOut(int id, uint32_t usec) {
|
|||
}
|
||||
|
||||
int APS5_VABI sceHttp2SslDisableOption(int id, uint32_t ssl_flags) {
|
||||
(void)id;
|
||||
(void)ssl_flags;
|
||||
return 0;
|
||||
return HandleIsLive(id) ? 0 : ERROR_INVALID_ID;
|
||||
}
|
||||
|
||||
int APS5_VABI sceHttp2SslEnableOption(int id, uint32_t ssl_flags) {
|
||||
(void)id;
|
||||
(void)ssl_flags;
|
||||
return 0;
|
||||
return HandleIsLive(id) ? 0 : ERROR_INVALID_ID;
|
||||
}
|
||||
|
||||
int APS5_VABI sceHttp2SetMinSslVersion(int id, uint32_t ssl_version) {
|
||||
(void)id;
|
||||
(void)ssl_version;
|
||||
return 0;
|
||||
return HandleIsLive(id) ? 0 : ERROR_INVALID_ID;
|
||||
}
|
||||
|
||||
int APS5_VABI sceHttp2Term(int lib_http2_ctx_id) {
|
||||
std::lock_guard lock(g_poolsMutex);
|
||||
g_pools.erase(lib_http2_ctx_id);
|
||||
std::lock_guard lock(g_handlesMutex);
|
||||
if (FindHandle(lib_http2_ctx_id, Http2Kind::Context) == nullptr) return ERROR_INVALID_ID;
|
||||
ReleaseOwnedHandles(lib_http2_ctx_id);
|
||||
g_handles.erase(lib_http2_ctx_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -260,13 +308,13 @@ int APS5_VABI sceHttp2AbortRequest(int req_id) {
|
|||
|
||||
int APS5_VABI sceHttp2GetMemoryPoolStats(int lib_http2_ctx_id, Http2MemoryPoolStats* stats) {
|
||||
if (stats == nullptr) return ERROR_NULL_POINTER;
|
||||
std::lock_guard lock(g_poolsMutex);
|
||||
const auto pool = g_pools.find(lib_http2_ctx_id);
|
||||
if (pool == g_pools.end()) {
|
||||
std::lock_guard lock(g_handlesMutex);
|
||||
const Http2Handle* handle = FindHandle(lib_http2_ctx_id, Http2Kind::Context);
|
||||
if (handle == nullptr) {
|
||||
std::memset(stats, 0, sizeof(*stats));
|
||||
return ERROR_INVALID_ID;
|
||||
}
|
||||
*stats = {pool->second, 0, 0, 0};
|
||||
*stats = {handle->poolSize, 0, 0, 0};
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -276,8 +324,11 @@ int APS5_VABI sceHttp2CookieFlush(int id) {
|
|||
}
|
||||
|
||||
int APS5_VABI sceHttp2CreateCookieBox(int lib_http2_ctx_id) {
|
||||
(void)lib_http2_ctx_id;
|
||||
return g_nextHandle.fetch_add(1, std::memory_order_relaxed);
|
||||
std::lock_guard lock(g_handlesMutex);
|
||||
if (FindHandle(lib_http2_ctx_id, Http2Kind::Context) == nullptr) return ERROR_INVALID_ID;
|
||||
const int id = g_nextHandle.fetch_add(1, std::memory_order_relaxed);
|
||||
g_handles[id] = {Http2Kind::CookieBox, lib_http2_ctx_id, 0};
|
||||
return id;
|
||||
}
|
||||
|
||||
int APS5_VABI sceHttp2SetCookieBox(int id, int cookie_box_id) {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,10 @@ int APS5_VABI sceHttp2SendRequest(int, const void*, std::size_t);
|
|||
int APS5_VABI sceHttp2SendRequestAsync(int, const void*, std::size_t, Http2AsyncOption*, void*);
|
||||
int APS5_VABI sceHttp2WaitAsync(int, Http2AsyncResult*, std::uint32_t*, void*);
|
||||
int APS5_VABI sceHttp2DeleteRequest(int);
|
||||
int APS5_VABI sceHttp2DeleteTemplate(int);
|
||||
int APS5_VABI sceHttp2SetMinSslVersion(int, std::uint32_t);
|
||||
int APS5_VABI sceHttp2SslEnableOption(int, std::uint32_t);
|
||||
int APS5_VABI sceHttp2SslDisableOption(int, std::uint32_t);
|
||||
int APS5_VABI sceHttp2Term(int);
|
||||
int APS5_VABI sceKernelCreateEqueue(KernelEqueue* eq, const char* name);
|
||||
int APS5_VABI sceKernelDeleteEqueue(KernelEqueue eq);
|
||||
|
|
@ -40,6 +44,14 @@ int APS5_VABI sceHttp2WebSocketSetPingInterval();
|
|||
|
||||
static void Require(bool value) { if (!value) std::abort(); }
|
||||
|
||||
static constexpr int InvalidId = static_cast<int>(0x817B1100);
|
||||
|
||||
static void RequireSslSetters(int id, int expected) {
|
||||
Require(sceHttp2SetMinSslVersion(id, 0) == expected);
|
||||
Require(sceHttp2SslEnableOption(id, 0) == expected);
|
||||
Require(sceHttp2SslDisableOption(id, 0) == expected);
|
||||
}
|
||||
|
||||
template <typename TCall>
|
||||
static bool Throws(TCall call) {
|
||||
try {
|
||||
|
|
@ -72,6 +84,50 @@ int main() {
|
|||
Require(sceHttp2CookieFlush(context) == 0);
|
||||
Require(sceHttp2SendRequest(request, nullptr, 0) == static_cast<int>(0x80436063));
|
||||
|
||||
RequireSslSetters(0, InvalidId);
|
||||
RequireSslSetters(-1, InvalidId);
|
||||
RequireSslSetters(0x7fffffff, InvalidId);
|
||||
RequireSslSetters(context, 0);
|
||||
RequireSslSetters(box, 0);
|
||||
RequireSslSetters(other, 0);
|
||||
RequireSslSetters(tmpl, 0);
|
||||
RequireSslSetters(request, 0);
|
||||
|
||||
const int doomedRequest = sceHttp2CreateRequestWithURL(tmpl, "GET", "https://example.com/doomed", 0);
|
||||
Require(doomedRequest > 0);
|
||||
Require(sceHttp2DeleteRequest(doomedRequest) == 0);
|
||||
RequireSslSetters(doomedRequest, InvalidId);
|
||||
Require(sceHttp2DeleteRequest(doomedRequest) == InvalidId);
|
||||
|
||||
const int doomedTemplate = sceHttp2CreateTemplate(context, "agent", 2, 0);
|
||||
Require(doomedTemplate > 0);
|
||||
const int doomedTemplateRequest = sceHttp2CreateRequestWithURL(doomedTemplate, "GET", "https://example.com/child", 0);
|
||||
Require(doomedTemplateRequest > 0);
|
||||
Require(sceHttp2DeleteTemplate(doomedTemplate) == 0);
|
||||
RequireSslSetters(doomedTemplate, InvalidId);
|
||||
RequireSslSetters(doomedTemplateRequest, InvalidId);
|
||||
Require(sceHttp2DeleteTemplate(doomedTemplate) == InvalidId);
|
||||
|
||||
const int doomedContext = sceHttp2Init(1, 1, 0x2000, 4);
|
||||
Require(doomedContext > 0);
|
||||
const int doomedContextBox = sceHttp2CreateCookieBox(doomedContext);
|
||||
const int doomedContextTemplate = sceHttp2CreateTemplate(doomedContext, "agent", 2, 0);
|
||||
Require(doomedContextBox > 0 && doomedContextTemplate > 0);
|
||||
const int doomedContextRequest = sceHttp2CreateRequestWithURL(doomedContextTemplate, "GET", "https://example.com/descendant", 0);
|
||||
Require(doomedContextRequest > 0);
|
||||
RequireSslSetters(doomedContext, 0);
|
||||
Require(sceHttp2Term(doomedContext) == 0);
|
||||
RequireSslSetters(doomedContext, InvalidId);
|
||||
RequireSslSetters(doomedContextBox, InvalidId);
|
||||
RequireSslSetters(doomedContextTemplate, InvalidId);
|
||||
RequireSslSetters(doomedContextRequest, InvalidId);
|
||||
Require(sceHttp2Term(doomedContext) == InvalidId);
|
||||
|
||||
const int freshContext = sceHttp2Init(1, 1, 0x2000, 4);
|
||||
Require(freshContext > 0);
|
||||
RequireSslSetters(freshContext, 0);
|
||||
Require(sceHttp2Term(freshContext) == 0);
|
||||
|
||||
KernelEqueue eq = 0;
|
||||
Require(sceKernelCreateEqueue(&eq, "http2") == 0);
|
||||
Require(sceKernelAddUserEventEdge(eq, request) == 0);
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [libSceAudio3d](../../core/libs/prx/libSceAudio3d/Export.cpp) - the port produces no sound: objects can be reserved and `sceAudio3dObjectSetAttributes` accepts their attributes, but the library has no bed exports or mixing to give them audio, the attributes have no effect and `sceAudio3dPortPush` only paces the queue at `granularity` samples per frame at 48 kHz. `sceAudio3dPortSetAttribute` accepts the late reverb level and the downmix spread attributes and ignores them. `sceAudio3dObjectSetAttributes` validates the port, the reserved object and the attribute array (an object id that was never reserved returns the invalid-object error, while shadPS4 returns success for it and the error only for a released object; neither is measured on a console), treating the reset-state attribute as valid with a null value and requiring a value for the others; the attribute identifiers and the value of each are not interpreted. `sceAudio3dPortFlush` drops the frames queued by `sceAudio3dPortAdvance` instead of mixing and submitting them to an output, as there is no output
|
||||
- [libSceTextToSpeech2](../../core/libs/prx/libSceTextToSpeech2/Export.cpp) has no speech engine: `sceTextToSpeech2Initialize` fails and the other exports throw. PPSA01317 skips its narration set-up when initialization fails
|
||||
- [libSceAvPlayer](../../core/libs/prx/libSceAvPlayer/Export.cpp): `sceAvPlayerSetLogCallback` accepts a callback that is never called, as the player produces no log messages, and `sceAvPlayerSetAvailableBandwidth` has no effect, as it governs HLS sources, which `sceAvPlayerAddSource` does not implement.
|
||||
- [libSceHttp2](../../core/libs/prx/libSceHttp2/Export.cpp) - no request reaches the network, so `sceHttp2SetMinSslVersion` has no TLS connection to restrict and returns success for any `id`, like `sceHttp2SslEnableOption` (prosper returns the invalid-id error for an unknown one)
|
||||
- [libSceHttp2](../../core/libs/prx/libSceHttp2/Export.cpp) - no request reaches the network, so `sceHttp2SetMinSslVersion` has no TLS connection to restrict and its value is ignored, as for `sceHttp2SslEnableOption` and `sceHttp2SslDisableOption`. The three setters validate `id` against the live handle registry and return `SCE_HTTP2_ERROR_INVALID_ID` (`0x817B1100`) for a handle that was never allocated, was deleted, or belonged to a terminated context, following [prosper](https://github.com/mattias800/prosper). Whether the console restricts the object kind these setters accept, and what it does with an id from a previous init/term cycle, is unverified, so any live handle kind is accepted and ids are never reused
|
||||
- [libSceNpTrophy2](../../core/libs/prx/libSceNpTrophy2/src/GameInfo.cpp): `sceNpTrophy2RegisterUnlockCallback` accepts a callback that is never called, as no trophy is ever unlocked, `sceNpTrophy2UnregisterUnlockCallback` only returns success, and `sceNpTrophy2ShowTrophyList` returns success without showing a list (there is no system trophy UI; seen on PPSA15552).
|
||||
- [sceAvPlayerSetTrickSpeed](../../core/libs/prx/libSceAvPlayer/src/Source.cpp) (libSceAvPlayer) with a negative speed runs the clock backwards but delivers no frames; when a forward speed is set again, playback resumes from the rewound time.
|
||||
- [sceRudpActivate](../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - there is no reliable-UDP stack on the host to activate, so it returns success without doing anything, as [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/rudp/rudp.cpp) does for its own `sceRudpActivate`. PPSA01325 calls it with no arguments during start-up; what the console activates, and whether it reports an error before `sceRudpInit`, is unverified
|
||||
|
|
|
|||
Loading…
Reference in New Issue