mirror of
https://github.com/vector-im/hydrogen-web.git
synced 2024-12-23 11:35:04 +01:00
prevent concurrent calls of OlmEncryption.encrypt to OOM olm wasm heap
This is being triggered by connecting to many call members at once, while encrypting the signaling messages. This keeps many olm.Session objects into memory at the same time, which makes olm run out of wasm heap memory.
This commit is contained in:
parent
bb2e63b05b
commit
7ce5cdfc4a
@ -21,6 +21,7 @@ import {createSessionEntry} from "./Session";
|
|||||||
import type {OlmMessage, OlmPayload, OlmEncryptedMessageContent} from "./types";
|
import type {OlmMessage, OlmPayload, OlmEncryptedMessageContent} from "./types";
|
||||||
import type {Account} from "../Account";
|
import type {Account} from "../Account";
|
||||||
import type {LockMap} from "../../../utils/LockMap";
|
import type {LockMap} from "../../../utils/LockMap";
|
||||||
|
import {Lock, MultiLock, ILock} from "../../../utils/Lock";
|
||||||
import type {Storage} from "../../storage/idb/Storage";
|
import type {Storage} from "../../storage/idb/Storage";
|
||||||
import type {Transaction} from "../../storage/idb/Transaction";
|
import type {Transaction} from "../../storage/idb/Transaction";
|
||||||
import type {DeviceIdentity} from "../../storage/idb/stores/DeviceIdentityStore";
|
import type {DeviceIdentity} from "../../storage/idb/stores/DeviceIdentityStore";
|
||||||
@ -62,6 +63,9 @@ const OTK_ALGORITHM = "signed_curve25519";
|
|||||||
const MAX_BATCH_SIZE = 20;
|
const MAX_BATCH_SIZE = 20;
|
||||||
|
|
||||||
export class Encryption {
|
export class Encryption {
|
||||||
|
|
||||||
|
private _batchLocks: Array<Lock>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly account: Account,
|
private readonly account: Account,
|
||||||
private readonly pickleKey: string,
|
private readonly pickleKey: string,
|
||||||
@ -71,15 +75,43 @@ export class Encryption {
|
|||||||
private readonly ownUserId: string,
|
private readonly ownUserId: string,
|
||||||
private readonly olmUtil: Olm.Utility,
|
private readonly olmUtil: Olm.Utility,
|
||||||
private readonly senderKeyLock: LockMap<string>
|
private readonly senderKeyLock: LockMap<string>
|
||||||
) {}
|
) {
|
||||||
|
this._batchLocks = new Array(MAX_BATCH_SIZE);
|
||||||
|
for (let i = 0; i < MAX_BATCH_SIZE; i += 1) {
|
||||||
|
this._batchLocks[i] = new Lock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A hack to prevent olm OOMing when `encrypt` is called several times concurrently,
|
||||||
|
* which is the case when encrypting voip signalling message to send over to_device.
|
||||||
|
* A better fix will be to extract the common bits from megolm/KeyLoader in a super class
|
||||||
|
* and have some sort of olm/SessionLoader that is shared between encryption and decryption
|
||||||
|
* and only keeps the olm session in wasm memory for a brief moment, like we already do for RoomKeys,
|
||||||
|
* and get the benefit of an optimal cache at the same time.
|
||||||
|
* */
|
||||||
|
private async _takeBatchLock(amount: number): Promise<ILock> {
|
||||||
|
const locks = this._batchLocks.filter(l => !l.isTaken).slice(0, amount);
|
||||||
|
if (locks.length < amount) {
|
||||||
|
const takenLocks = this._batchLocks.filter(l => l.isTaken).slice(0, amount - locks.length);
|
||||||
|
locks.push(...takenLocks);
|
||||||
|
}
|
||||||
|
await Promise.all(locks.map(l => l.take()));
|
||||||
|
return new MultiLock(locks);
|
||||||
|
}
|
||||||
|
|
||||||
async encrypt(type: string, content: Record<string, any>, devices: DeviceIdentity[], hsApi: HomeServerApi, log: ILogItem): Promise<EncryptedMessage[]> {
|
async encrypt(type: string, content: Record<string, any>, devices: DeviceIdentity[], hsApi: HomeServerApi, log: ILogItem): Promise<EncryptedMessage[]> {
|
||||||
let messages: EncryptedMessage[] = [];
|
let messages: EncryptedMessage[] = [];
|
||||||
for (let i = 0; i < devices.length ; i += MAX_BATCH_SIZE) {
|
for (let i = 0; i < devices.length ; i += MAX_BATCH_SIZE) {
|
||||||
const batchDevices = devices.slice(i, i + MAX_BATCH_SIZE);
|
const batchDevices = devices.slice(i, i + MAX_BATCH_SIZE);
|
||||||
|
const batchLock = await this._takeBatchLock(batchDevices.length);
|
||||||
|
try {
|
||||||
const batchMessages = await this._encryptForMaxDevices(type, content, batchDevices, hsApi, log);
|
const batchMessages = await this._encryptForMaxDevices(type, content, batchDevices, hsApi, log);
|
||||||
messages = messages.concat(batchMessages);
|
messages = messages.concat(batchMessages);
|
||||||
}
|
}
|
||||||
|
finally {
|
||||||
|
batchLock.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user