I *UPDATED* the issue main text so nobody gets confused.

Issue: When you set the 404 page to user/login and you are a logged in user and calling a page that did not exist, Drupal will create an infinite loop.

Drupals standardbehavior on paths which are not found is to save the path in $_GET['destination'] and if set redirect to the page which is set in variable 'site_404' (see code). If that variable returns user/login, the logincode will then realize, that the user is already logged in and will without displaying the suer/login page redirect to the saved original page request from $_GET['destination']. This of cause, was not found in the first place and the loop starts again.

        // Check for and return a fast 404 page if configured.
        drupal_fast_404();

        // Keep old path for reference, and to allow forms to redirect to it.
        if (!isset($_GET['destination'])) {
          $_GET['destination'] = $_GET['q'];
        }

        $path = drupal_get_normal_path(variable_get('site_404', ''));
        if ($path && $path != $_GET['q']) {

The change to user/login happend in this rows and yes it is our fault. But I think the thinking that, if somebody requests a page which is not there sees the user/login page sound not so bad. We were developing a drupal site which NEEDs to have logged in users.

So this indeed is reproducable with vanilla drupal. Just put user/login in the 404 textfield, save and request the page /foobarblubb as authenticated user. There you have your infinite loop.

Btw. the 404 and 403 fields have no distinction of logged und anonymous 404 pages, so this error might happen to someone else als well. All caused by the rewrite of $_GET['q'] and $_GET['destination'].

A hint below the fields could be usefull or a check in the core. Not sure.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

ro-no-lo’s picture

To clarify: The part that I get redirected to user/login while already logged in is strange and wrong.

pounard’s picture

Project: Core Library » Drupal core
Version: 7.x-2.x-dev » 7.x-dev
Component: Code » base system

Not an issue for this module.

marcingy’s picture

Category: bug » support

This does not sound like a core bug but a badily configured module somewhere. Moving to support request.

ro-no-lo’s picture

Category: bug » support
FileSize
30.01 KB

FOUND THE CAUSE

Because it bothered me once again, I debugged it again:

        $path = drupal_get_normal_path(variable_get('site_404', ''));
        if ($path && $path != $_GET['q']) {

The change to user/login happend in this rows and yes it is our fault. But I think the thinking that, if somebody requests a page which is not there sees the user/login page sound not so bad. We were developing a drupal site which NEEDs to have logged in users.

So this indeed is reproducable with vanilla drupal. Just put user/login in the 404 textfield, save and request the page /foobarblubb as authenticated user. There you have your infinite loop.

Btw. the 404 and 403 fields have no distinction of logged und anonymous 404 pages, so this error might happen to someone else als well. All caused by the rewrite of $_GET['q'] and $_GET['destination'].

A hint below the fields could be usefull or a check in the core. Not sure.

ro-no-lo’s picture

Category: support » bug

I changed it back to bug report, so that maybe someone checks if that is indeed an issue who should be avoided in the future - even by accidential input of user/login, which is an absolut valid path in the system for anonymous users as a 404 page.

Feel free to change the category back.

pounard’s picture

Category: support » bug

I agree that if this can be reproduced with a vanilla core just by setting the site_404 to 'user/login' this is bug. As this behavior is now known, we should either forbidden this path to be set in this variable, either fix it in order to avoid infinite redirect loops.

ro-no-lo’s picture

Just thinking:

I belive that Drupal needs a solution, which scales to more paths not just 'user/login'. There might me modules which defines other pathes for the same functionality as 'user/login'. Another approach might be usefull.

In the assumption that '' will allways work or at least should really always work. An infinite loop counter could be added. Which tries the drupal_goto() in case of a 404 for X-times. and if a certain amount is exceeded, it redirects to '' with a message and/or watchdog entry. I looked into the common.inc if I could hack something together real quick, but common.inc#2510 was not as easy to understand for me in that case.

I hope someone with more core router knowledge can do it.

Something like this, but I think thats not that good:

        if (!isset($_GET['destination'])) {
          variable_set('redirect_infinite_loop_route', $_GET['q']);
          variable_set('redirect_infinite_loop_counter', variable_get('redirect_infinite_loop_counter', 0) + 1);
          $_GET['destination'] = $_GET['q'];
        }

        $infinite_route   = variable_get('redirect_infinite_loop_route', '--dummy--');
        $infinite_counter = variable_get('redirect_infinite_loop_counter', 0);
        if ($infinite_counter > 3 && $infinite_route == $_GET['q']) {
          $_GET['destination'] = '<front>';
          variable_set('redirect_infinite_loop_counter', 0);
          variable_set('redirect_infinite_loop_route', '--dummy--');
          watchdog('core', 'Infinite Loop detected at !url', $_GET['q']);
          drupal_set_message(t('Infinite Loop detected'));
        }
pounard’s picture

It's not easy to track redirect loop from the server, it assumes that we need to store data either in the session or another place that is linked to the user currently browsing (which is exactly what is the session for, we could also just use a cookie thought): it may break the lazy session cookie sending algorithm, this kind of session extra usage would leave the user hit pages with no cache where it should deliver a cached page.

ro-no-lo’s picture

The code I added worked for my simple example. Might be a good start. Because the code is only in the MENU_NOT_FOUND section added.

Hmm your right. This needs to be user bound and therefore the variable table is not a good idea.

pounard’s picture

Yes because multiple requests from different clients could conflict, and also because variables system performance is critical, and data in this table has a front cache on top of it, rebuild operations have a really negative performance impact, it should not be used for volatile or mutant data.

pounard’s picture

Issue summary: View changes

Updated Entry

superspring’s picture

Version: 7.x-dev » 8.x-dev
Status: Active » Needs review
FileSize
1.46 KB

This patch prevents infinite redirects by storing which URLs have already gone through when redirecting with 404s.

Status: Needs review » Needs work

The last submitted patch, infinite_404_redirect-1672282-11.patch, failed testing.

superspring’s picture

Status: Needs work » Needs review
marcingy’s picture

Status: Needs review » Needs work

We can't store 404s in session my opinion as this leads to a state where anonymous users end up with sessions which was purposely removed in d7.

superspring’s picture

Hey @marcingy,

To have no sessions what-so-ever this could be rewritten to use cookies. Is this the type of fix that you're looking for?

marcingy’s picture

Cookies are also not ideal either because of implications it also has for proxies.

The issue is caused by

$items['user/login'] = array(
    'title' => 'Log in',
    'access callback' => 'user_is_anonymous',
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );

So I would make it so as the variable' site_404 can't be set to user/login. Instead the path of user can be used which will either send the user to their profile page or present a login form as appropriate. The infinte loop will not happen as the access callback allows anyone to access the page. Ie never allow the inifinte redirect to exist in the first place.

marcingy’s picture

Issue summary: View changes

Removed the distracting texts, so the main issue is clearly pointed out.

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.8.x-dev

Drupal 8.6.x will not receive any further development aside from security fixes. Bug reports should be targeted against the 8.8.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.9.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

quietone’s picture

Version: 8.9.x-dev » 7.x-dev
Issue tags: +Bug Smash Initiative

I set the 404 page to user/login and tested by accessing a non existing node as user 1 and as anonymous. There was no infinite loop in Drupal 9.2.x or Drupal 8.9.13 but there was in Drupal 7.79-dev.

Moving to D7 issue queue.