-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[SecurityBundle] Fixed a memory leak in SecurityBundle\Security\FirewallMap #22605
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
@@ -78,6 +78,8 @@ public function onKernelFinishRequest(FinishRequestEvent $event) | |||
{ | |||
$request = $event->getRequest(); | |||
|
|||
$this->map->detachListeners($event->getRequest()); |
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 may not always work (think of other implementations of the FirewallMapInterface
).
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.
Fixed by replacing the implementation.
* | ||
* @param Request $request | ||
*/ | ||
public function detachListeners(Request $request); |
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.
Adding a new method to an interface is a BC break and not allowed by our BC promise.
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.
Fixed by replacing the implementation.
But doesn't the issue concern only the SecurityBundle map due to its per-request cache? Wouldn't mapping contexts by Request object hash instead of Request instances be enough, changing the |
@chalasr: The basic problem is the using of FirewallMapInterface to refer to long-lived services. There is no idea of destroying/detaching of FirewallMap from inside Firewall, because both of them are services and live forever. |
@udavka : I don't get how it can improve anything when talking about the SecurityBundle
Which leak? Regarding the component |
@ogizanagi Symfony is able to process many requests without restarting the whole framework. This idea is used in long-lived applications. Services are not leaks, because they are not recreated on every request. But Request is not a service and must be destroyed after the processing. |
Indeed, as the previous request is destroyed as this point. Good point.
Sure, but not sure how much it can really affect long-living applications, as its one array item (per request though) just holding references.
Looks acceptable to me, and would allow to be merged as a bugfix in lower branches. |
private function detachListeners(Request $request) | ||
{ | ||
unset($this->contexts[$request]); | ||
} |
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.
IMO you should directly unset the request in the onKernelFinishRequest
method, that would make one less method call...
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.
fixed
@@ -75,4 +78,34 @@ private function getFirewallContext(Request $request) | |||
} | |||
} | |||
} | |||
|
|||
/** | |||
* @param FinishRequestEvent $event |
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.
To remove, as it does not bring anything more than the signature.
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.
fixed
/** | ||
* Get subscribed events. | ||
* | ||
* @return array Subscribed events |
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.
The whole docblock should be replaced by
/**
* {@inheritdoc}
*/
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.
fixed
Since the matching context relates to the request, this information should have been cached inside the request parameters. This inside the // code in 3.2
private function getFirewallContext(Request $request)
{
if ($this->contexts->contains($request)) {
return $this->contexts[$request];
}
foreach ($this->map as $contextId => $requestMatcher) {
if (null === $requestMatcher || $requestMatcher->matches($request)) {
return $this->contexts[$request] = $this->container->get($contextId);
}
}
} We can remove the // proposal
private function getFirewallContext(Request $request)
{
if ($request->attributes->has('_security_firewall_context')) {
return $this->container->get($request->attributes->get('_security_firewall_context'));
}
foreach ($this->map as $contextId => $requestMatcher) {
if (null === $requestMatcher || $requestMatcher->matches($request)) {
$request->attributes->set('_security_firewall_context', $contextId);
return $this->container->get($contextId);
}
}
} |
Indeed, @GromNaN's suggestion looks even better to me :) |
Status: needs work |
Closing in favor of #22943 |
…he request parameters (GromNaN) This PR was squashed before being merged into the 3.2 branch (closes #22943). Discussion ---------- [SecurityBundle] Move cache of the firewall context into the request parameters Following [this proposal](#22605 (comment)). Since the matching context relates to the request, this information should have been cached inside the request parameters. | Q | A | ------------- | --- | Branch? | 3.2 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #22605 | License | MIT | Doc PR | n/a * Avoid memory leak when handling multiple requests * Adding the new request parameter `_firewall_context` might be considered as a breaking change. That adds a new "public" property that could be used by end developers. Commits ------- b3203cb [SecurityBundle] Move cache of the firewall context into the request parameters
There is a memory leak in Symfony\Bundle\SecurityBundle\Security\FirewallMap, which leads to not autodestroy Symfony\Component\HttpFoundation\Request object after the request has been proceeded. This object is quite big and uses many internal subobjects, and as a result this leads to an essential memory leak, which can affect long-lived applications like php-pm.
How to reproduce: create a default sample Symfony application, add a destructor for the Request class, add unset($request); at the end of web/app.php and make sure that this destructor is not called after unset().
With this fix the destructor is called immediately after unset(), and that means no memory leak for the Request object.