-
Notifications
You must be signed in to change notification settings - Fork 16.3k
fix: do not initialize extension system in in-memory sessions #22772
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
Conversation
We don't allow loading extensions in temp session for the same reason #22090 But at the same time if an app wants to run completely off an in memory session it should be allowed. In this case we just have to make sure the extension dependencies are not loaded for in memory session rather than always creating a permanent session. |
9429809
to
de66b34
Compare
@deepak1556 Took an alternative approach of just not initializing any of the extension stuff in contexts that are in memory |
Sorry for the delay, looked further into the original crash, it was happening because the keyed service was trying to register on a browser context that was destroyed. Even if the extension system is registered on an in memory context, the keyed services that need persistence will rely on We currently do the following: BrowserContext* ElectronExtensionsBrowserClient::GetOriginalContext(
BrowserContext* context) {
DCHECK(context);
if (context->IsOffTheRecord()) {
return ElectronBrowserContext::From("", false); // cause of the crash
} else {
return context;
}
}
https://gist.github.com/deepak1556/fccbfd62d4a5ada87dc9b5aca58f75dc |
@deepak1556 This change ensures that we don't register the extensions system on in-memory contexts (and therefore also fixes this). Do we want to create (and leak) a default session when an. in-memory session is created (and that in-memory session can't use extensions APIs anyway)? |
By leak do you mean the persistent stuff being created on the user's file system ?
Yeah but realized there are other components that rely just on extension system being initialized without the use of extension api we expose, for ex: PDF viewer. Chrome is slightly different but it does piggyback an incognito context on top of a permanent context to make these things work. So maybe its better we follow that as well ? But if we are good with ignoring features like PDF viewer for in-memory context then we can stick with the current solution. |
@nornagon Thoughts on the above? |
IMO:
|
It's not entirely clear from this discussion, will that still be possible? AFAIK the following used to accomplish that: new BrowserWindow({
webPreferences: {
partition: 'memory_partition_1',
},
}); |
@bughit that will continue to work; however, the default session will be created behind the scenes. If there's a compelling reason not to create the default session, we could potentially consider alternatives, but I'm not aware of any such reason. |
So this default session is a persistent one and will be reading/writing to the userData path?
My use-case is multiple parallel instances of the app. They have the same userData. Usually they use memory partitions, When they use persistent partitions they are distinct per process, so there's no interference. If they now start sharing a persistent default session, isn't that going to be a problem? The ability to run without any persistence seems like a valuable feature, worth keeping. |
Only data relevant to the session will be written to disk, so if you never open a window or make a request in the default session, no meaningful data will be written. This change doesn't alter observable behavior other than perhaps creating a few empty files and directories in the userData directory that otherwise might not have been created.
This ought to continue to work. As above, the persistence or otherwise of actual session data isn't affected by this change; it's just creating an empty/unused session behind the scenes, because Chromium's infrastructure expects it. |
My understanding is that chrome does not support sharing its data directory among multiple browser instances. So even though this default sessions will not be used explicitly, it seems to me its implicit use, which at least involves file creation and ultimately whatever else chrome decides to do with it, is not instance-safe. What if two instances start creating it at the same time? I would expect no parallel multi-instance write to the shared default session to be supported/safe. |
I ran the following test with 9.0, which creates but does not use a default session 'use strict';
const {app, BrowserWindow, session} = require('electron'),
Path = require('path');
let win;
app.setPath('userData', Path.join(__dirname, 'chrome_user_data'));
app.once('ready', async () => {
session.defaultSession;
win = new BrowserWindow({
webPreferences: {
partition: 'memory_partition_1',
},
});
}); And captured the writing with procmon
Does this test capture what your defaultSession creation will do? If so, I can't imagine this is multi-instance safe. If you believe it is, please explain, otherwise please continue supporting runing without a persistent defaultSession. |
@nornagon Regardless of how this shakes out, it's important to understand the ramifications, so to that end, could you (or others in this thread) please comment on my previous two posts? |
@bughit I agree that we should be sure not to break multiple instances of Electron running at the same time. I don't have enough context right now to say for sure how Chromium's internals would handle multiple processes accessing that directory, or whether this PR would break that use case. |
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.
see above, we need to confirm that the use-case for multiple instances of electron isn't broken.
Here's a confirmation from chromium docs
also an internal bug report mentions it WebView should make sure it's not used multiple times at once from the same data dir
|
// Guard usages of extension_system() with !IsOffTheRecord() | ||
// There is no extension system for in-memory sessions |
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.
DCHECK
perhaps?
describe('crash cases', () => { | ||
afterEach(() => { | ||
for (const child of children) { | ||
child.kill() | ||
} | ||
expect(children).to.have.lengthOf(0, 'all child processes should have exited cleanly') | ||
children.length = 0 | ||
}) | ||
const cases = fs.readdirSync(fixturePath) | ||
|
||
for (const crashCase of cases) { | ||
it(`the "${crashCase}" case should not crash`, () => { | ||
const fixture = path.resolve(fixturePath, crashCase) | ||
const argsFile = path.resolve(fixture, 'electron.args') | ||
const args = [fixture] | ||
if (fs.existsSync(argsFile)) { | ||
args.push(...fs.readFileSync(argsFile, 'utf8').trim().split('\n')) | ||
} | ||
return runFixtureAndEnsureCleanExit(args) | ||
}) | ||
} | ||
}) |
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.
this should go in the extensions spec file
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.
This was starting a new thing / easy way to add "this used to crash" style specs instead of scattering them around with re-implementation of the bootstrapping logic. Now you can just add fixtures to the crash cases folder and assert they no longer crash
Release Notes Persisted
|
I have automatically backported this PR to "9-x-y", please check out #23472 |
I took that to mean that you would to avoid a persistent defaultSession which is not multi-instance safe, unless the app chose to use it.
However, looking at v18 it's created by default and it doesn't appear possible to avoid it. So can this be considered a bug and be addressed? I am comparing with v4.2 which did not create a persistent default session unless a BrowserWindows was configured to use it. |
Fixes #22414
Some parts of Chrome in the extension logic expect at least one persistent (not off the record) BrowserContext to exist even when creating an off-the-record context. This change ensures we don't initialize extensions in a context that does not meet that expectation. It also adds some handy new infra in our test suite to make it really easy to test crash regressions in the future 👍
Notes: fixed crash that could occur when calling
session.fromPartition
inside the ready event