-
Notifications
You must be signed in to change notification settings - Fork 639
Add mutex to getSnapState to prevent concurrent decryption
#3234
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| { | ||
| "branches": 93.34, | ||
| "functions": 97.36, | ||
| "functions": 97.37, | ||
| "lines": 98.33, | ||
| "statements": 98.07 | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -286,6 +286,11 @@ export type SnapRuntimeData = { | |
| * A mutex to prevent concurrent state updates. | ||
| */ | ||
| stateMutex: Mutex; | ||
|
|
||
| /** | ||
| * A mutex to prevent concurrent state decryption. | ||
| */ | ||
| getStateMutex: Mutex; | ||
| }; | ||
|
|
||
| export type SnapError = { | ||
|
|
@@ -2046,33 +2051,36 @@ export class SnapController extends BaseController< | |
| */ | ||
| async getSnapState(snapId: SnapId, encrypted: boolean): Promise<Json> { | ||
| const runtime = this.#getRuntimeExpect(snapId); | ||
| const cachedState = encrypted ? runtime.state : runtime.unencryptedState; | ||
| return await runtime.getStateMutex.runExclusive(async () => { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With this cached requests would still have to queue to access the cache, right? That seems less than ideal 🤔
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it work if we have a
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you mean by nesting in this case?
Yeah, but cached access should be much quicker, so I don't think it's a big issue?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's try this as-is and we can always move some logic outside of the mutex block |
||
| const cachedState = encrypted ? runtime.state : runtime.unencryptedState; | ||
|
|
||
| if (cachedState !== undefined) { | ||
| return cachedState; | ||
| } | ||
| if (cachedState !== undefined) { | ||
| return cachedState; | ||
| } | ||
|
|
||
| const state = encrypted | ||
| ? this.state.snapStates[snapId] | ||
| : this.state.unencryptedSnapStates[snapId]; | ||
| const state = encrypted | ||
| ? this.state.snapStates[snapId] | ||
| : this.state.unencryptedSnapStates[snapId]; | ||
|
|
||
| if (state === null || state === undefined) { | ||
| return null; | ||
| } | ||
| if (state === null || state === undefined) { | ||
| return null; | ||
| } | ||
|
|
||
| if (!encrypted) { | ||
| // For performance reasons, we do not validate that the state is JSON, | ||
| // since we control serialization. | ||
| const json = JSON.parse(state); | ||
| runtime.unencryptedState = json; | ||
| if (!encrypted) { | ||
| // For performance reasons, we do not validate that the state is JSON, | ||
| // since we control serialization. | ||
| const json = JSON.parse(state); | ||
| runtime.unencryptedState = json; | ||
|
|
||
| return json; | ||
| } | ||
| return json; | ||
| } | ||
|
|
||
| const decrypted = await this.#decryptSnapState(snapId, state); | ||
| runtime.state = decrypted; | ||
| const decrypted = await this.#decryptSnapState(snapId, state); | ||
| // eslint-disable-next-line require-atomic-updates | ||
| runtime.state = decrypted; | ||
|
|
||
| return decrypted; | ||
| return decrypted; | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -3968,6 +3976,7 @@ export class SnapController extends BaseController< | |
| interpreter, | ||
| stopping: false, | ||
| stateMutex: new Mutex(), | ||
| getStateMutex: new Mutex(), | ||
| }); | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wondering if we could re-use the
stateMutexThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It could, but I'm not sure if it should? We can do encryption and decryption at the same time, they don't depend on each other.