Fix Riot Client crash when cancelling CFHost resolution

This commit is contained in:
Hitalo M. authored and Kalah committed 2026-10-16 09:09:28 +02:00
1 parent 07c3d09bcc
commit 67626612ac
9 files changed
+112 -2

No files matched your search

+1 -1
View File
@@ -26,7 +26,7 @@ This is a development snapshot, not a finished launcher. Riot and League clients
## Included
- Pinned upstream revisions and patches for 29 Darling components, including Metal/Indium, AppKit, QuartzCore, CoreAudio, the kernel shim and darlingserver.
- Pinned upstream revisions and patches for 30 Darling components, including Metal/Indium, AppKit, QuartzCore, CoreAudio, the kernel shim and darlingserver.
- Event-poll presentation and GPU synchronization fixes, floating-point-safe GL resolvers, multi-window presentation and nullable LaunchServices error handling.
- Reusable server receive buffers and Darwin allocator hints translated to Linux lazy reclamation.
- Build/install/launch wrappers, atomic incremental installers and standalone regression probes.
+16
View File
@@ -394,3 +394,19 @@ Observe host CPU/thread scheduling, GPU counters, temperatures and memory pressu
Compare similar scene, ability and camera activity with the same power settings (and correct charger on a laptop). Do not report a paused, dead, unfocused or empty scene as a fight benchmark. If an allowed host measurement supplies frame times, report p99 in milliseconds; its reciprocal is percentile-equivalent FPS, not the mean of the slowest 1% of frames. Manual FPS readings alone do not establish p99 frame times.
The CPU symbolizer offers `--leaf-only` because the host DWARF unwinder can drop guest Mach-O stacks. Successfully unwound samples alone are not a representative profile. Local API lockfiles, account data and raw logs must stay outside version control.
## CFHost cancellation regression
After configuring and building CFNetwork, build and run the isolated probe:
```sh
python3 build/runtime/tools/build-riot-cfhost-cancel-check.py
python3 build/runtime/tools/run-riot-native-check.py riot-cfhost-cancel-check
```
Run against the installed CFNetwork override. Expect
`CFHOST_CANCEL_CHECK cancel callback lifetime repeated pass=1`. The unpatched
library traps in `CFRetain(NULL)`; the runner checks the success marker because
Darling can return status zero even when the guest traps. The probe uses localhost
and exercises 100 cancellations with both retained and early-released caller
references, without launching or inspecting Riot.
+14
View File
@@ -56,6 +56,20 @@ The installer fetcher no longer treats a missing archive `Info.plist` or the inf
There is also a separate known compatibility gap: native LaunchAgent watch registration can return HTTP 409 after the League install path is accepted, leaving the download idle. A historical inspector hook handles this and interrupted `pending_verify` states in 28 mock cases, but that hook is disabled in normal launches and excluded from the manual workflow. There is no verified runtime-only replacement for that handoff in this snapshot. Do not assume the native permission and launcher fixes resolve every idle Install case, or enable debugger/API automation to work around it.
## Riot closes after a network lookup is cancelled
Two local crashes were traced to `CFNetwork::_HostCancel` calling `CFRetain(NULL)`.
`CFHostCancelInfoResolution` scheduled its cancellation callback with an empty
context, so processing that source triggered `SIGTRAP` in CoreFoundation and
terminated RiotClientServices. The UI helpers then exited as a consequence.
The CFNetwork patch supplies the host and retains it until the cancellation source
is invalidated. The standalone `riot-cfhost-cancel-check` reproduces the old trap
and passes 100 cancellations with the fix, including releasing the caller's host
reference before callback delivery. This establishes the cancellation fix; a long
Riot session still needs confirmation. With both clients and the game closed,
run `./scripts/update.sh` to install it, then `sh scripts/run.sh`.
## UI crashes after a button opens an external link
LaunchServices dereferenced a nullable error-output pointer in six paths. The patch checks the pointer itself and populates supplied outputs; the standalone regression covers missing handlers and null outputs. Electron opens LaunchServices through an absolute CoreServices subframework path, so the paired installer also replaces that binary in the stopped guest prefix. Updating only `RuntimeFrameworks/LaunchServices.framework` can leave the crash unfixed.
+17
View File
@@ -0,0 +1,17 @@
diff --git a/src/Host/CFHost.c b/src/Host/CFHost.c
index 91c0d0f..d6e3daf 100644
--- a/src/Host/CFHost.c
+++ b/src/Host/CFHost.c
@@ -2221,9 +2221,9 @@ CFHostCancelInfoResolution(CFHostRef theHost, CFHostInfoType info) {
CFRunLoopSourceContext ctxt = {
0, // version
- NULL, // info
- NULL, // retain
- NULL, // release
+ host, // info
+ CFRetain, // retain
+ CFRelease, // release
NULL, // copyDescription
NULL, // equal
NULL, // hash
+22
View File
@@ -0,0 +1,22 @@
#!/usr/bin/env python3
"""Build the standalone CFHost cancellation regression probe."""
from pathlib import Path
import shlex
import subprocess
project = Path(__file__).resolve().parent.parent
framework = project / 'build/darling-src/build/src/external/cfnetwork/src'
flags = {}
for line in (framework / 'CMakeFiles/CFNetwork.dir/flags.make').read_text().splitlines():
if line.startswith('C_') and ' = ' in line:
name, value = line.split(' = ', 1)
flags[name] = shlex.split(value)
name = 'riot-cfhost-cancel-check'
obj = project / ('build/' + name + '.o')
subprocess.run(['clang', *flags['C_DEFINES'], *flags['C_INCLUDES'], *flags['C_FLAGS'],
'-O2', '-Wno-macro-redefined', '-c', str(project / ('tools/' + name + '.c')),
'-o', str(obj)], check=True)
subprocess.run(['clang', '-target', 'x86_64-apple-macosx11.0', '-fuse-ld=lld', '-nostdlib',
'-isysroot', '@DARLING_SDK@', str(obj), '-lSystem', '-framework', 'CFNetwork',
'-framework', 'CoreFoundation', '-framework', 'Foundation',
'-o', str(project / ('build/' + name))], check=True)
+4 -1
View File
@@ -8,6 +8,7 @@ guest_home="${RIOT_GUEST_HOME:-$HOME/.darling@GUEST_HOME@}"
make -C "$build_dir/src/external/xnu/darling/src/libsystem_kernel" system_kernel -j8
make -C "$build_dir/src/external/libplatform" platform -j8
make -C "$build_dir/src/external/corefoundation" CoreFoundation_x86_64 -j8
make -C "$build_dir/src/external/cfnetwork/src" CFNetwork -j8
make -C "$build_dir/src/external/configd/SystemConfiguration.fproj" SystemConfiguration -j8
make -C "$build_dir/src/external/cocotron/QuartzCore" QuartzCore -j8
make -C "$build_dir/src/external/cocotron/AppKit" AppKit X11_backend -j8
@@ -26,7 +27,7 @@ darling shutdown || true
mv "$project_dir/build/libdarling-xshm.next.so" "$project_dir/build/libdarling-xshm.so"
mv "$guest_home/libDarlingXShm.next.dylib" "$guest_home/libDarlingXShm.dylib"
mkdir -p "$guest_home/RuntimeFrameworks"
for framework in AppKit QuartzCore CoreFoundation SystemConfiguration; do
for framework in AppKit QuartzCore CoreFoundation SystemConfiguration CFNetwork; do
if [ ! -d "$guest_home/RuntimeFrameworks/$framework.framework" ]; then
cp -a "@DARLING_SDK@/System/Library/Frameworks/$framework.framework" "$guest_home/RuntimeFrameworks/"
fi
@@ -43,6 +44,8 @@ atomic_install "$build_dir/src/external/corefoundation/CoreFoundation" "$guest_h
atomic_install "$build_dir/src/external/configd/SystemConfiguration.fproj/SystemConfiguration" "$guest_home/RuntimeFrameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration"
atomic_install "$build_dir/src/external/cfnetwork/src/CFNetwork" "$guest_home/RuntimeFrameworks/CFNetwork.framework/Versions/A/CFNetwork"
mkdir -p "$guest_home/RuntimeLibraries"
atomic_install "$build_dir/src/external/libplatform/libsystem_platform.dylib" "$guest_home/RuntimeLibraries/libsystem_platform.dylib"
atomic_install "$build_dir/src/external/xnu/darling/src/libsystem_kernel/libsystem_kernel.dylib" "$guest_home/RuntimeLibraries/libsystem_kernel.dylib"
+29
View File
@@ -0,0 +1,29 @@
#include <CoreFoundation/CoreFoundation.h>
#include <CFNetwork/CFHost.h>
#include <stdio.h>
static unsigned callbacks;
static void completed(CFHostRef host, CFHostInfoType type,
const CFStreamError *error, void *info) {
if (host && type == kCFHostAddresses && info == &callbacks)
++callbacks;
}
int main(void) {
CFHostClientContext context = {0, &callbacks, NULL, NULL, NULL};
for (unsigned i = 0; i < 100; ++i) {
CFHostRef host = CFHostCreateWithName(NULL, CFSTR("localhost"));
CFStreamError error = {0};
if (!host || !CFHostSetClient(host, completed, &context)) return 1;
CFHostScheduleWithRunLoop(host, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
if (!CFHostStartInfoResolution(host, kCFHostAddresses, &error)) return 2;
CFHostCancelInfoResolution(host, kCFHostAddresses);
/* The cancel source must keep its host alive until delivery. */
if (i % 2) CFRelease(host);
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.05, false);
if (callbacks != i + 1) return 3;
if (!(i % 2)) CFRelease(host);
}
puts("CFHOST_CANCEL_CHECK cancel callback lifetime repeated pass=1");
return 0;
}
+1
View File
@@ -9,6 +9,7 @@ import tempfile
root = Path(__file__).resolve().parent.parent
checks = {
"riot-cfhost-cancel-check": "CFHOST_CANCEL_CHECK",
"riot-launchservices-null-error-check": "LAUNCHSERVICES_NULL_ERROR",
"m327-cgl-resolver-check": "GL_RESOLVER",
"riot-mach-exception-state-check": "MACH_EXCEPTION_STATE",
+8
View File
@@ -305,6 +305,14 @@
"private-include/indium/submit-thread.private.hpp",
"private-include/indium/waits.private.hpp"
]
},
{
"path": "src/external/cfnetwork",
"revision": "e7e3db881008d883f82914765a72ce842bcba735",
"repository": "https://github.com/darlinghq/darling-cfnetwork.git",
"patch": "patches/cfnetwork.patch",
"modified_files": 1,
"new_files": []
}
],
"omitted": [