mirror of
https://github.com/TecharoHQ/anubis.git
synced 2025-08-03 09:48:08 -04:00
fix(web): amend future leak on proof of work solution (#879)
Possible fix for #877 In some cases, the parallel solution finder in Anubis could cause all of the worker promises to leak due to the fact the promises were being improperly terminated. A recursion bomb happens in the following scenario: 1. A worker sends a message indicating it found a solution to the proof of work challenge. 2. The `onmessage` handler for that worker calls `terminate()` 3. Inside `terminate()`, the parent process loops through all other workers and calls `w.terminate()` on them. 4. It's possible that terminating a worker could lead to the `onerror` event handler. 5. This would create a recursive loop of `onmessage` -> `terminate` -> `onerror` -> `terminate` -> `onerror` and so on. This infinite recursion quickly consumes all available stack space, but this has never been noticed in development because all of my computers have at least 64Gi of ram provisioned to them under the axiom paying for more ram is cheaper than paying in my time spent having to work around not having enough ram. Additionally, ia32 has a smaller base stack size, which means that they will run into this issue much sooner than users on other CPU architectures will. The fix adds a boolean `settled` flag to prevent termination from running more than once. Signed-off-by: Xe Iaso <me@xeiaso.net>
This commit is contained in:
parent
3e1aaa6273
commit
5e8ebaeb5d
@ -14,7 +14,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
<!-- This changes the project to: -->
|
||||
|
||||
- Expired records are now properly removed from bbolt databases ([#848](https://github.com/TecharoHQ/anubis/pull/848)).
|
||||
|
||||
- Fix hanging on service restart ([#853](https://github.com/TecharoHQ/anubis/issues/853))
|
||||
|
||||
### Added
|
||||
@ -25,6 +24,12 @@ Anubis now supports these new languages:
|
||||
|
||||
Anubis now supports the [`missingHeader`](./admin/configuration/expressions.mdx#missingHeader) to assert the absence of headers in requests.
|
||||
|
||||
### Fixes
|
||||
|
||||
#### Fix potential memory leak when discovering a solution
|
||||
|
||||
In some cases, the parallel solution finder in Anubis could cause all of the worker promises to leak due to the fact the promises were being improperly terminated. This was fixed by having Anubis debounce worker termination instead of allowing it to potentially recurse infinitely.
|
||||
|
||||
## v1.21.0: Minfilia Warde
|
||||
|
||||
> Please, be at ease. You are among friends here.
|
||||
|
@ -14,32 +14,42 @@ export default function process(
|
||||
);
|
||||
|
||||
let worker = new Worker(webWorkerURL);
|
||||
const terminate = () => {
|
||||
let settled = false;
|
||||
|
||||
const cleanup = () => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
worker.terminate();
|
||||
if (signal != null) {
|
||||
// clean up listener to avoid memory leak
|
||||
signal.removeEventListener("abort", terminate);
|
||||
if (signal.aborted) {
|
||||
console.log("PoW aborted");
|
||||
reject(false);
|
||||
}
|
||||
signal.removeEventListener("abort", onAbort);
|
||||
}
|
||||
URL.revokeObjectURL(webWorkerURL);
|
||||
};
|
||||
|
||||
const onAbort = () => {
|
||||
console.log("PoW aborted");
|
||||
cleanup();
|
||||
reject(new DOMException("Aborted", "AbortError"));
|
||||
};
|
||||
|
||||
if (signal != null) {
|
||||
signal.addEventListener("abort", terminate, { once: true });
|
||||
if (signal.aborted) {
|
||||
return onAbort();
|
||||
}
|
||||
signal.addEventListener("abort", onAbort, { once: true });
|
||||
}
|
||||
|
||||
worker.onmessage = (event) => {
|
||||
if (typeof event.data === "number") {
|
||||
progressCallback?.(event.data);
|
||||
} else {
|
||||
terminate();
|
||||
cleanup();
|
||||
resolve(event.data);
|
||||
}
|
||||
};
|
||||
|
||||
worker.onerror = (event) => {
|
||||
terminate();
|
||||
cleanup();
|
||||
reject(event);
|
||||
};
|
||||
|
||||
@ -47,8 +57,6 @@ export default function process(
|
||||
data,
|
||||
difficulty,
|
||||
});
|
||||
|
||||
URL.revokeObjectURL(webWorkerURL);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -12,19 +12,31 @@ export default function process(
|
||||
);
|
||||
|
||||
const workers = [];
|
||||
const terminate = () => {
|
||||
let settled = false;
|
||||
|
||||
const cleanup = () => {
|
||||
if (settled) {
|
||||
return;
|
||||
}
|
||||
settled = true;
|
||||
workers.forEach((w) => w.terminate());
|
||||
if (signal != null) {
|
||||
// clean up listener to avoid memory leak
|
||||
signal.removeEventListener("abort", terminate);
|
||||
if (signal.aborted) {
|
||||
console.log("PoW aborted");
|
||||
reject(false);
|
||||
}
|
||||
signal.removeEventListener("abort", onAbort);
|
||||
}
|
||||
URL.revokeObjectURL(webWorkerURL);
|
||||
};
|
||||
|
||||
const onAbort = () => {
|
||||
console.log("PoW aborted");
|
||||
cleanup();
|
||||
reject(new DOMException("Aborted", "AbortError"));
|
||||
};
|
||||
|
||||
if (signal != null) {
|
||||
signal.addEventListener("abort", terminate, { once: true });
|
||||
if (signal.aborted) {
|
||||
return onAbort();
|
||||
}
|
||||
signal.addEventListener("abort", onAbort, { once: true });
|
||||
}
|
||||
|
||||
for (let i = 0; i < threads; i++) {
|
||||
@ -34,13 +46,13 @@ export default function process(
|
||||
if (typeof event.data === "number") {
|
||||
progressCallback?.(event.data);
|
||||
} else {
|
||||
terminate();
|
||||
cleanup();
|
||||
resolve(event.data);
|
||||
}
|
||||
};
|
||||
|
||||
worker.onerror = (event) => {
|
||||
terminate();
|
||||
cleanup();
|
||||
reject(event);
|
||||
};
|
||||
|
||||
@ -53,8 +65,6 @@ export default function process(
|
||||
|
||||
workers.push(worker);
|
||||
}
|
||||
|
||||
URL.revokeObjectURL(webWorkerURL);
|
||||
});
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user