diff --git a/.changeset/tidy-clubs-wink.md b/.changeset/tidy-clubs-wink.md new file mode 100644 index 0000000000..f60a2d4139 --- /dev/null +++ b/.changeset/tidy-clubs-wink.md @@ -0,0 +1,5 @@ +--- +"@lynx-js/web-core": patch +--- + +fix: avoid to do use-after-free for rust instance diff --git a/packages/web-platform/web-core/ts/client/mainthread/Background.ts b/packages/web-platform/web-core/ts/client/mainthread/Background.ts index 3151838fdc..0544ecaf8e 100644 --- a/packages/web-platform/web-core/ts/client/mainthread/Background.ts +++ b/packages/web-platform/web-core/ts/client/mainthread/Background.ts @@ -70,6 +70,7 @@ export class BackgroundThread implements AsyncDisposable { #batchSendTimingInfo: RpcCallType; readonly jsContext: LynxCrossThreadContext; + #messagePort?: MessagePort; readonly postTimingFlags: RpcCallType; readonly sendGlobalEvent: RpcCallType; @@ -166,6 +167,7 @@ export class BackgroundThread implements AsyncDisposable { } as WorkerStartMessage, [messageChannel.port2], ); + this.#messagePort = messageChannel.port1; this.#rpc.setMessagePort(messageChannel.port1); } @@ -303,6 +305,8 @@ export class BackgroundThread implements AsyncDisposable { } else { this.#webWorker?.terminate(); } + this.#messagePort?.close(); + this.#messagePort = undefined; this.#nextMacroTask && clearTimeout(this.#nextMacroTask); } } diff --git a/packages/web-platform/web-core/ts/client/mainthread/ExposureServices.ts b/packages/web-platform/web-core/ts/client/mainthread/ExposureServices.ts index 26812dd3de..28468e168b 100644 --- a/packages/web-platform/web-core/ts/client/mainthread/ExposureServices.ts +++ b/packages/web-platform/web-core/ts/client/mainthread/ExposureServices.ts @@ -300,4 +300,19 @@ export class ExposureServices { } this.#isExposureServiceOn = toEnable; } + + dispose() { + this.#exposureEnabledElementsToIntersectionObserver.forEach((observer) => { + observer.disconnect(); + }); + this.#exposureEnabledElementsToIntersectionObserver.clear(); + this.#exposureEnabledElementsToOldExposureIdAttributeValue.clear(); + this.#exposedElements.clear(); + if (this.#globalExposureEventBatchTimer) { + clearTimeout(this.#globalExposureEventBatchTimer); + this.#globalExposureEventBatchTimer = null; + } + this.#globalExposureEventCache = []; + this.#globalDisexposureEventCache = []; + } } diff --git a/packages/web-platform/web-core/ts/client/mainthread/LynxViewInstance.ts b/packages/web-platform/web-core/ts/client/mainthread/LynxViewInstance.ts index 0b5ca3a2c5..5e4ca1f284 100644 --- a/packages/web-platform/web-core/ts/client/mainthread/LynxViewInstance.ts +++ b/packages/web-platform/web-core/ts/client/mainthread/LynxViewInstance.ts @@ -27,6 +27,7 @@ import { loadAllWebElements } from '../webElementsDynamicLoader.js'; // @ts-expect-error import IN_SHADOW_CSS_MODERN from '../../../css/in_shadow.css?inline'; import type { LynxViewElement } from './LynxView.js'; +import { requestIdleCallbackImpl } from './utils/requestIdleCallback.js'; loadAllWebElements().catch((e) => { console.error('[lynx-web] Failed to load web elements', e); }); @@ -297,7 +298,10 @@ export class LynxViewInstance implements AsyncDisposable { } async [Symbol.asyncDispose]() { - this.mtsWasmBinding.dispose(); await this.backgroundThread[Symbol.asyncDispose](); + this.exposureServices.dispose(); + requestIdleCallbackImpl(() => { + this.mtsWasmBinding.dispose(); + }); } } diff --git a/packages/web-platform/web-core/ts/client/mainthread/elementAPIs/WASMJSBinding.ts b/packages/web-platform/web-core/ts/client/mainthread/elementAPIs/WASMJSBinding.ts index e42416a7d9..d73045b80f 100644 --- a/packages/web-platform/web-core/ts/client/mainthread/elementAPIs/WASMJSBinding.ts +++ b/packages/web-platform/web-core/ts/client/mainthread/elementAPIs/WASMJSBinding.ts @@ -205,10 +205,7 @@ export class WASMJSBinding implements RustMainthreadContextBinding { this.lynxViewInstance.rootDom.removeEventListener( eventName, this.#commonEventHandler, - { - passive: true, - capture: true, - } as any, + true, ); } this.#addedEventListeners.clear(); diff --git a/packages/web-platform/web-core/ts/client/mainthread/elementAPIs/createElementAPI.ts b/packages/web-platform/web-core/ts/client/mainthread/elementAPIs/createElementAPI.ts index e3ed99fc33..40b22f9cd7 100644 --- a/packages/web-platform/web-core/ts/client/mainthread/elementAPIs/createElementAPI.ts +++ b/packages/web-platform/web-core/ts/client/mainthread/elementAPIs/createElementAPI.ts @@ -83,6 +83,8 @@ export function createElementAPI( disposed = true; if (wasmContext) { wasmContext.free(); + // @ts-expect-error It's better to throw an Error than triggering an use-after-free of rust struct + wasmContext = null; } page = undefined; timingFlags.length = 0;