Problem/Motivation

After updating to Drupal 10.3, I get the following error when trying to visit any page on my site:

Error: Maximum call stack size of 8355840 bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion? in Drupal\Component\DependencyInjection\Container->get() (line 129 of core/lib/Drupal/Component/DependencyInjection/Container.php).

Drupal\Component\DependencyInjection\Container->get('cache_context.user.is_super_user') (Line: 222)
Drupal\Core\Cache\Context\CacheContextsManager->getService('user.is_super_user') (Line: 184)
Drupal\Core\Cache\Context\CacheContextsManager->optimizeTokens(Array) (Line: 110)
Drupal\Core\Cache\Context\CacheContextsManager->convertTokensToKeys(Array) (Line: 242)
Drupal\Core\Cache\VariationCache->createCacheIdFast(Array, Object) (Line: 173)
Drupal\Core\Cache\VariationCache->getRedirectChain(Array, Object) (Line: 35)
Drupal\Core\Cache\VariationCache->get(Array, Object) (Line: 80)
Drupal\Core\Session\AccessPolicyProcessor->processAccessPolicies(Object) (Line: 126)
Drupal\Core\Session\PermissionsHashGenerator->getCacheableMetadata(Object) (Line: 53)
Drupal\Core\Cache\Context\AccountPermissionsCacheContext->getCacheableMetadata(NULL) (Line: 184)
Drupal\Core\Cache\Context\CacheContextsManager->optimizeTokens(Array) (Line: 110)
Drupal\Core\Cache\Context\CacheContextsManager->convertTokensToKeys(Array) (Line: 219)
Drupal\Core\Cache\VariationCache->createCacheId(Array, Object) (Line: 57)
Drupal\Core\Cache\VariationCache->set(Array, Object, Object, Object) (Line: 94)
Drupal\Core\Session\AccessPolicyProcessor->processAccessPolicies(Object) (Line: 126)
Drupal\Core\Session\PermissionsHashGenerator->getCacheableMetadata(Object) (Line: 53)
Drupal\Core\Cache\Context\AccountPermissionsCacheContext->getCacheableMetadata(NULL) (Line: 184)
Drupal\Core\Cache\Context\CacheContextsManager->optimizeTokens(Array) (Line: 110)
Drupal\Core\Cache\Context\CacheContextsManager->convertTokensToKeys(Array) (Line: 219)
Drupal\Core\Cache\VariationCache->createCacheId(Array, Object) (Line: 57)
Drupal\Core\Cache\VariationCache->set(Array, Object, Object, Object) (Line: 94)
Drupal\Core\Session\AccessPolicyProcessor->processAccessPolicies(Object) (Line: 126)
Drupal\Core\Session\PermissionsHashGenerator->getCacheableMetadata(Object) (Line: 53)
Drupal\Core\Cache\Context\AccountPermissionsCacheContext->getCacheableMetadata(NULL) (Line: 184)
Drupal\Core\Cache\Context\CacheContextsManager->optimizeTokens(Array) (Line: 110)
Drupal\Core\Cache\Context\CacheContextsManager->convertTokensToKeys(Array) (Line: 219)
Drupal\Core\Cache\VariationCache->createCacheId(Array, Object) (Line: 57)
Drupal\Core\Cache\VariationCache->set(Array, Object, Object, Object) (Line: 94)
Drupal\Core\Session\AccessPolicyProcessor->processAccessPolicies(Object) (Line: 126)
Drupal\Core\Session\PermissionsHashGenerator->getCacheableMetadata(Object) (Line: 53)
Drupal\Core\Cache\Context\AccountPermissionsCacheContext->getCacheableMetadata(NULL) (Line: 184)
Drupal\Core\Cache\Context\CacheContextsManager->optimizeTokens(Array) (Line: 110)
Drupal\Core\Cache\Context\CacheContextsManager->convertTokensToKeys(Array) (Line: 219)
Drupal\Core\Cache\VariationCache->createCacheId(Array, Object) (Line: 57)
Drupal\Core\Cache\VariationCache->set(Array, Object, Object, Object) (Line: 94)

The following part of the call stack is being repeated indefinitely:

Drupal\Core\Session\AccessPolicyProcessor->processAccessPolicies(Object) (Line: 126)
Drupal\Core\Session\PermissionsHashGenerator->getCacheableMetadata(Object) (Line: 53)
Drupal\Core\Cache\Context\AccountPermissionsCacheContext->getCacheableMetadata(NULL) (Line: 184)
Drupal\Core\Cache\Context\CacheContextsManager->optimizeTokens(Array) (Line: 110)
Drupal\Core\Cache\Context\CacheContextsManager->convertTokensToKeys(Array) (Line: 219)
Drupal\Core\Cache\VariationCache->createCacheId(Array, Object) (Line: 57)
Drupal\Core\Cache\VariationCache->set(Array, Object, Object, Object) (Line: 94)

Steps to reproduce

Might be a combination of this core version with certain custom/contrib modules or config, but I haven't been able to figure that out.

Proposed resolution

I was able to come up with a workaround: in AccessPolicyProcessor::processAccessPolicies(), if the resulting CacheableMetadata contains the 'user.permissions' cache context, remove it. This cache key invokes PermissionsHashGenerator::getCacheableMetadata(), which in turn invokes AccessPolicyProcessor::processAccessPolicies(), creating an infinite loop.

Remaining tasks

Find a better fix if necessary.

User interface changes

API changes

Data model changes

Release notes snippet

Issue fork drupal-3461270

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

DieterHolvoet created an issue. See original summary.

dieterholvoet’s picture

Status: Active » Needs review
cilefen’s picture

Is xdebug enabled? I remember there were issues with that, although this of course may be unrelated.

dieterholvoet’s picture

Yes, Xdebug is enabled.

dieterholvoet’s picture

Oh and might be worth mentioning, the user.permissions cache context is attached to the role entity, which at some point is added as cacheable dependency.

smustgrave’s picture

Version: 10.3.x-dev » 11.x-dev
Status: Needs review » Needs work
Issue tags: +Needs tests

MR should be updated for 11.x

Nothing major stuck out but definitely seems like something that will need test coverage

geek-merlin’s picture

@Dieter
I debugged a similar thing lately, it looks like the new permission negitiation code is not robust against some config override cacheability.
Can you grep "implements ConfigFactoryOverrideInterface" modules?

dieterholvoet’s picture

That's it! The following class in my custom code seems to be the culprit. I don't think I'm doing anything wrong there though?

<?php

namespace Drupal\my_module\Config;

use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Config\ConfigFactoryOverrideInterface;
use Drupal\Core\Config\StorageInterface;
use Drupal\Core\Session\AccountProxyInterface;

class MenuBreadcrumbConfigOverrides implements ConfigFactoryOverrideInterface
{
    public function __construct(
        protected AccountProxyInterface $currentUser,
    ) {
    }

    public function loadOverrides($names): array
    {
        $overrides = [];

        if (!in_array('menu_breadcrumb.settings', $names, true)) {
            return $overrides;
        }

        if ($this->currentUser->hasPermission('access toolbar')) {
            if ($this->currentUser->hasPermission('access editor toolbar')) {
                $overrides['menu_breadcrumb.settings']['menu_breadcrumb_menus']['editor']['enabled'] = 1;
            } else {
                $overrides['menu_breadcrumb.settings']['menu_breadcrumb_menus']['admin']['enabled'] = 1;
            }
        }

        return $overrides;
    }

    public function getCacheSuffix(): string
    {
        return 'MenuBreadcrumbConfigOverrides';
    }

    public function getCacheableMetadata($name): CacheableMetadata
    {
        return (new CacheableMetadata())
            ->addCacheContexts(['user.permissions']);
    }

    public function createConfigObject($name, $collection = StorageInterface::DEFAULT_COLLECTION)
    {
        return null;
    }
}
geek-merlin’s picture

Yo! I made the same omission in translation_bliss module, now fixed:
You need a $name check in getCacheableMetadata.

That said, imho it's still a core bug if it WSODs on too much config cachability.

dieterholvoet’s picture

You're right, that fixes it for me. Thanks!

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.