Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork9.7k
[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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Conversation
| { | ||
| $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 theFirewallMapInterface).
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 | ||
| */ | ||
| publicfunctiondetachListeners(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.
chalasr commentedMay 2, 2017
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 |
udavka commentedMay 2, 2017
@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. |
ogizanagi commentedMay 12, 2017 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
@udavka : I don't get how it can improve anything when talking about the SecurityBundle
Which leak? Regarding the component |
udavka commentedMay 12, 2017 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
@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. |
ogizanagi commentedMay 12, 2017 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
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. |
| privatefunctiondetachListeners(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 theonKernelFinishRequest 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
| } | ||
| /** | ||
| * @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
GromNaN commentedMay 13, 2017 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Since the matching context relates to the request, this information should have been cached inside the request parameters. This inside the // code in 3.2privatefunctiongetFirewallContext(Request$request) {if ($this->contexts->contains($request)) {return$this->contexts[$request]; }foreach ($this->mapas$contextId =>$requestMatcher) {if (null ===$requestMatcher ||$requestMatcher->matches($request)) {return$this->contexts[$request] =$this->container->get($contextId); } } } We can remove the // proposalprivatefunctiongetFirewallContext(Request$request) {if ($request->attributes->has('_security_firewall_context')) {return$this->container->get($request->attributes->get('_security_firewall_context')); }foreach ($this->mapas$contextId =>$requestMatcher) {if (null ===$requestMatcher ||$requestMatcher->matches($request)) {$request->attributes->set('_security_firewall_context',$contextId);return$this->container->get($contextId); } } } |
ogizanagi commentedMay 14, 2017 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Indeed,@GromNaN's suggestion looks even better to me :) |
nicolas-grekas commentedMay 29, 2017
Status: needs work |
fabpot commentedJun 9, 2017
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 parametersFollowing [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
Uh oh!
There was an error while loading.Please reload this page.
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.