mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
It asserted that it was the first call, so net::Init - which is documented as safe to call repeatedly, and is - needed a bool of its own purely to guard this one line. That's bookkeeping the library may as well do itself, and the WSA half of the same function already says as much: it doesn't track anything because WSAStartup counts its own references. So a repeat call does nothing, and net::Init loses g_naettInitialized. Asking HTTPSAvailable() twice costs nothing - on Linux it's a cached dlopen result and everywhere else it's a constant.
9.0 KiB
9.0 KiB
naett in PPSSPP
Vendored copy of naett by Erik Agsjö (MIT licensed, see LICENSE).
Upstream version: v0.3.3 (5f695cfa9fcbf30668a4d3ac4b4abf1cd89a1302, 2024-04-13)
This used to be a git submodule. It's now in-tree because upstream has been dormant since April 2024 and we need to carry local changes (see below). It's ~1500 lines of C, which is smaller than several other things we already vendor.
Local changes
Keep this list up to date - it's what makes it possible to move to a newer upstream later.
- Dropped the generated single-file amalgam (
naett.c) andsrc/amalgam.h, along withexample/andtestrig/. We buildsrc/*.cdirectly, so that the file you edit is the file that gets compiled. src/naett_internal.h: added#include "../naett.h". The amalgam includednaett.hahead of everything else; building the sources directly, each one needs it.src/naett_linux.c: added#include <stdio.h>/#include <stdlib.h>. It usesexit/calloc/realloc/free/fprintfbut never included either header - in the amalgam it got them fromnaett_core.cfurther up the concatenation. Building it on its own is an error with a modern compiler.src/naett_curl.h/src/naett_curl.c: new, ours. Loads libcurl withdlopeninstead of linking against it, so libcurl stays a soft dependency at runtime.naett_linux.cincludes that header in place of<curl/curl.h>.src/naett_linux.c: replaced thepanic()that calledexit(1)on a pipe orcurl_multi_performfailure. Taking the whole emulator down because a download failed isn't acceptable, so the backend now disables itself and requests complete withnaettGenericError.src/naett_linux.c:CURLINFO_RESPONSE_CODEwrites along, andres->codeis anint- upstream passed&res->codestraight tocurl_easy_getinfo, writing 8 bytes into 4. Reads into alonglocal now.src/naett_linux.c:curl_easy_setoptis varargs and takes alongfor these options; upstream passedintliterals andintvariables, which is UB on LP64 (and what curl's own typecheck macros warn about). They're1L/(long)now.src/naett_core.c:naettFreenever freedoptions.userAgent, though it'sstrdup'd by the same setter asmethod. Leaked once per request for every caller that sets a user agent, which we do on all of them.src/naett_core.c:defaultBodyWriterdoubled anintcapacity until it fit, which is signed overflow on a large response, and used thereallocresult without checking it - losing the old pointer and thenmemcpying through NULL. Grows inint64_tagainstINT_MAXand reports failure by returning short, which every caller already treats as an error.src/naett_linux.c:headerCallbackonly handed itsstrndupto the header list when the line had a colon, and leaked it otherwise. curl passes the status line and the blank line that ends the header block, so that leaked at least twice per response.src/naett_osx.c: the delegate class was built withobjc_allocateClassPairand then used without ever callingobjc_registerClassPair, which the runtime requires before the class can be instantiated. Registered now, after the methods and ivar are added.src/naett_osx.c: the response header arrays were VLAs sized from the server's header count - unbounded stack use from network data, and a zero-length VLA when a response had no headers. They're heap allocations now, skipped entirely when there are none.src/naett_osx.c: theNSURLSessionwas stored in the response without aretain, though the autorelease pool it came from is drained before returning. Retained, and released innaettPlatformCloseResponse.src/naett_objc.h:addMethod/addIvarreported failure withassertonly, so in release a delegate could silently come up without its methods. They print as well now.src/naett_win.c: the header sizing call only reports a size when it fails withERROR_INSUFFICIENT_BUFFER; on any other failure the size stayed zero andunpackHeadersranwcslenover amalloc(0)block. Checked, and the second query's result is checked too.src/naett_win.c:winToUTF8/winFromUTF8/wcsndupcould all return NULL and every caller used the result unchecked -packHeadersmost visibly, whose result is indexed asheaders[0]. All checked now.src/naett_win.c:res->bytesLeftis unsigned, so a read longer than the announced count wrapped it into an enormous value and kept the read loop running.src/naett_linux.c: the worker read the queuedCURL*out of the pipe into the start of its buffer while tracking a fill position, so a short read would have resumed mid-pointer and handed curl a mangled handle. Only ever safe because a write that size to a pipe is atomic.src/naett_linux.c: the easy handle is removed from the multi before being cleaned up, which is what curl asks for.curl_multi_remove_handleandcurl_multi_cleanupwere added to the dlopen table innaett_curl.h/naett_curl.cfor this.src/naett_linux.c:workerRunningis written by the worker and read by the request path, so it's anatomic_intnow rather than a plainint.src/naett_linux.c: thewritethat hands a request to the worker was unchecked - a failed one meant a request that never ran and never completed, so the caller pollednaettCompleteforever. Also retries onEINTR, andcurl_easy_initfailure is handled.src/naett_linux.c: the read and write callbacks returned the body callbacks'intstraight to curl, which reads it as asize_t- a negative arrived as an enormous count instead of an error.src/naett_linux.c: the multi handle and the pipe leaked when init failed partway.src/naett_android.c:getEnvcalledAttachCurrentThreadand nothing ever detached.processRequestdetaches its own thread, butnaettPlatformInitRequest/FreeRequestrun on the caller's, and a thread that exits while attached is fatal on Android. They attach only if the thread wasn't already, and detach when they're done.src/naett_android.c:pthread_create's result was ignored - with no worker, nothing setscompleteand the caller pollsnaettCompleteforever.src/naett_android.c:getOutputStreamcan throw, and the calls after it ran with the exception still pending, which isn't allowed for most of JNI. Checked now.src/naett_android.c:GetMethodIDreturns NULL for a method it can't find, and calling with a NULLjmethodIDaborts the VM; the header loop could also handGetStringUTFCharsa null value for a header with no entries.src/naett_core.c:naettCloseclearedres->requestbefore calling the backend's close, which is the one thing the backend needs - the WinHTTP handles hang off the request. The backend goes first now.naett.h: documented that a response should be complete before it's closed. Only the Android backend really cancels and waits.src/naett_win.c:naettPlatformCloseResponsewas empty, so the status callback kept the freed response as its context. Unhooks the callback and closes the request handle. Not a full cancel - a callback already running isn't waited for, which would need theWINHTTP_CALLBACK_STATUS_HANDLE_CLOSINGhandshake.src/naett_osx.c:invalidateAndCancelreturns before the session lets go of its delegate, so the delegate's back pointer to the response is cleared first, anddidReceiveDatachecks it (asdidCompleteWithErroralready did).src/naett_osx.c/src/naett_android.c: both threw away the body writer's return value. Windows and Linux already treat a short write as a failed request - it's how the default writer reports it couldn't grow, and how we cancel a transfer - so on those two a short write silently truncated the body and still looked like a success. Apple also cancels the task, or the data just keeps arriving.naett.h: documented what the body writer's return value means, since three of the four backends' behaviour depends on it.src/naett_win.c: a short write from the body writer marked the request complete and then queued another read anyway, so the transfer carried on and WinHTTP kept writing into a response the caller was by then free to close. It stops there now, and the callback returns early for anything raised after the request is complete.src/naett_core.c:naettMakeonly asserted that the request wasn't NULL, and the request constructors return NULL when the platform can't set one up - a URL it can't parse, say. In a release build that walked into a null dereference. Returns NULL instead.src/naett_osx.c: the delegate callbacks ignore a response that's already complete, so a cancellation we asked for doesn't overwrite the error that caused it.src/naett_core.c/naett.h:naettInitasserted it was only ever called once, so a caller that can be entered more than again needed a flag purely to guard it. A repeat call is a no-op now, the wayWSAStartupbehaves, andnet::Initdropped itsg_naettInitialized.