Problem/Motivation
Vanilla Drupal 7 typically goes through its bootstrap phases in this order:
https://git.drupalcode.org/project/drupal/blob/7.67/includes/bootstrap.i...
* - DRUPAL_BOOTSTRAP_CONFIGURATION: Initializes configuration.
* - DRUPAL_BOOTSTRAP_PAGE_CACHE: Tries to serve a cached page.
* - DRUPAL_BOOTSTRAP_DATABASE: Initializes the database layer.
* - DRUPAL_BOOTSTRAP_VARIABLES: Initializes the variable system.
* - DRUPAL_BOOTSTRAP_SESSION: Initializes session handling.
* - DRUPAL_BOOTSTRAP_PAGE_HEADER: Sets up the page header.
* - DRUPAL_BOOTSTRAP_LANGUAGE: Finds out the language of the page.
* - DRUPAL_BOOTSTRAP_FULL: Fully loads Drupal. Validates and fixes input data.
To handle SACORE-2018-004 new "request sanitizer" code was added to the end of the DRUPAL_BOOTSTRAP_CONFIGURATION phase:
https://git.drupalcode.org/project/drupal/blob/7.67/includes/bootstrap.i...
// Initialize the configuration, including variables from settings.php.
drupal_settings_initialize();
// Sanitize unsafe keys from the request.
require_once DRUPAL_ROOT . '/includes/request-sanitizer.inc';
DrupalRequestSanitizer::sanitize();
}
However, the domain module in D7 works by calling its own bootstrap phases from the end of settings.php, which is processed just before the new request sanitizer code.
The domain bootstrap code needs access to the database and it therefore invokes later phases of core's bootstrap - initially DRUPAL_BOOTSTRAP_DATABASE:
https://git.drupalcode.org/project/domain/blob/7.x-3.x/domain.bootstrap....
That results in other core bootstrap phases being invoked before drupal_settings_initialize() returns and execution reaches the new request sanitizer code.
One of these phases invoked "out of order" is DRUPAL_BOOTSTRAP_VARIABLES:
https://git.drupalcode.org/project/drupal/blob/7.67/includes/bootstrap.i...
That checks if there's a destination query parameter, and if so sanitizes it using the new class:
// Use the DrupalRequestSanitizer to ensure that the destination's query
// parameters are not dangerous.
if (isset($_GET['destination'])) {
DrupalRequestSanitizer::cleanDestination();
}
However, if the last couple of lines of the DRUPAL_BOOTSTRAP_CONFIGURATION phase have not yet been reached, the new code has not yet been included.
This can result in a fatal error as the new class has not yet been defined. This is illustrated by the following stack trace:
PHP Fatal error: Class 'DrupalRequestSanitizer' not found in /path/to/d7/includes/bootstrap.inc on line 2784
PHP Stack trace:
PHP 1. {main}() /path/to/d7/index.php:0
PHP 2. drupal_bootstrap() /path/to/d7/index.php:20
PHP 3. _drupal_bootstrap_configuration() /path/to/d7/includes/bootstrap.inc:2508
PHP 4. drupal_settings_initialize() /path/to/d7/includes/bootstrap.inc:2634
PHP 5. include_once() /path/to/d7/includes/bootstrap.inc:747
PHP 6. include() /path/to/d7/sites/default/settings.php:706
PHP 7. domain_bootstrap() /path/to/d7/sites/all/modules/contrib/domain/settings.inc:26
PHP 8. _domain_bootstrap() /path/to/d7/sites/all/modules/contrib/domain/domain.bootstrap.inc:70
PHP 9. drupal_bootstrap() /path/to/d7/sites/all/modules/contrib/domain/domain.bootstrap.inc:112
PHP 10. _drupal_bootstrap_page_cache() /path/to/d7/includes/bootstrap.inc:2512
PHP 11. drupal_bootstrap() /path/to/d7/includes/bootstrap.inc:2658
PHP 12. _drupal_bootstrap_variables() /path/to/d7/includes/bootstrap.inc:2520
Note this error is avoided if there's already an entry for the new class in the registry as it's then autoloaded.
Proposed resolution
Moving this line:
require_once DRUPAL_ROOT . '/includes/request-sanitizer.inc';
...slightly earlier in drupal_bootstrap() avoids the situation where the new class is used before it's been defined if something like the domain module calls the bootstrap phases in a different order.
Remaining tasks
- Review the patch.
- Commit the patch.
User interface changes
None.
API changes
None.
Data model changes
None.
Release notes snippet
None?
Original report by @dvandijk
With the Domain module active the bootstrap phase doesn't include the includes/request-sanitizer.inc file when a ?destination= URL parameter is present. Therefore resulting in a 500 error on all pages with the ?destination parameter.
The fix is adding an extra require_once on line 2784 in bootstrap.inc (see patch).
| Comment | File | Size | Author |
|---|---|---|---|
| #9 | 2966335-9.patch | 715 bytes | mcdruid |
Comments
Comment #2
mo6I confirmed the problem with D7 and domain and verified that this patch works.
Comment #3
steven buteneers commentedPlease provide steps to reproduce, on our platform running domain access on the current stable version we do not encounter this problem.
Comment #4
mcdruid commentedI've not been able to reproduce this with some quick basic tests, but I can see how it might happen.
The domain include which starts its own bootstrap is typically placed at the end of
settings.php...and
settings.phpis processed as part ofDRUPAL_BOOTSTRAP_CONFIGURATIONwhich calls:https://api.drupal.org/api/drupal/includes%21bootstrap.inc/function/_dru...
...which includes the new
request-sanitizer.inc:...but only after settings have been processed. I suppose if the domain module's bootstrap calls
DRUPAL_BOOTSTRAP_VARIABLESbeforedrupal_settings_initialize()returns, then theDrupalRequestSanitizerclass would not be available when it's called from_drupal_bootstrap_variables().Rather than
hackingpatching core for this, I'd recommend placing the include before the one for domain'ssettings.incat the end ofsettings.phpe.g.:...or something along those lines.
In terms of being able to reproduce the error, which of domain's sub-modules do you have enabled? I've tried with just domain itself and domain_conf and have yet to see an issue with requests including a
destinationparam.Comment #5
johan den hollander commentedWe experienced a 500 error when deleting a file from the admin/content/file page.
After including the require_once DRUPAL_ROOT . '/includes/request-sanitizer.inc'; part in settings.php as suggested by @mcdruid this error is gone...
Comment #6
mcdruid commentedI debugged this a little on a site with domain enabled which was hitting a Fatal error for requests which include a destination param, and was eventually able to reproduce the problem on a test site; here's a stack trace:
It looks like the problem happens when there's no entry in the registry for the
DrupalRequestSanitizerclass.If the class is in the registry:
...it gets autoloaded when it's called in
_drupal_bootstrap_variables()despite the fact that therequest-sanitizer.inchas not yet been explicitly included/required.If there's no entry for the class in the registry, we hit a Fatal error.
I was able to reproduce this by removing the entry in the registry (and clearing the
lookup_cachefromcache_bootstrap). Having done that, a request to my test site which includes a destination param causes the 500 Fatal error as the class fails to autoload.I'm looking into whether a simple cache clear (or a registry rebuild) is enough to fix this.
Comment #7
mcdruid commentedI verified that rebuilding the registry (e.g. perhaps with registry_rebuild) so that an entry exists for the
DrupalRequestSanitizerclass resolves this problem.It can be a bit of a messy process though (see the project page - make sure you take db backups etc..)
The workaround of explicitly doing a
require_once includes/request-sanitizer.incbefore the domain module's include in settings.php seems fairly harmless, and may be less painful than trying to ensure the registry contains an entry to allow the class to autoload.I'd argue this issue should probably move to the domain module's queue, as I don't really think it's core's fault that domain "hotwires" the bootstrap process.
Comment #8
David_Rothstein commentedI added a link to this to the 7.59 release notes in the Known Issues section now.
I think a core patch might be reasonable for this. (Triggering a higher bootstrap phase within the bootstrap process is something that should be expected to work.)
The patch itself looks pretty good to me. Another option might just be to move the existing require_once a little earlier, so that the file is already included before the settings.php file is ever loaded. I don't have a strong preference between the two.
Comment #9
mcdruid commentedOk, so here's a patch which moves the
require_onceto before the call to_drupal_bootstrap_configuration().As @David_Rothstein says, there's not much difference between this and the original patch, but this means one require instead of two, and there are already a couple of other files required before the function calls in the switch / case within
drupal_bootstrap(), so this is perhaps more consistent.Comment #10
mcdruid commentedJust noting that while this issue is worked on, I believe the settings.php workaround remains valid, and it shouldn't break things if a change is committed and released in D7 (obviously it could be removed once it was no longer necessary).
Comment #11
4kant commentedIn my case ( I got thes errors after updating core to 7.59 ) it was enough to update ctools module (or/and entity module).
Comment #12
mcdruid commented@4kant I can't tell for sure, but I suspect if that fixed your site it would have been because your registry was rebuilt, after which the "missing" class would be autoloaded.
That's quite likely to be stopping a lot of sites being affected by this AFAICS.
Comment #13
othermachines commentedWe just got a report about this problem. The client was uploading a PDF via the "Files" interface provided by file_entity. As a temporary fix we've modified settings.php as suggested in #4 and the error vanished. Thanks!
Comment #14
4kant commentedA supplement to #11: in another project with domain access module it was already clearing the cache that solved it.
Comment #15
sano commentedI was experiencing this error when attempting to log into a site (as user with uid = 1) with Domain module enabled. The #4 advice solved the problem. Thank you.
Comment #16
drupalganesh commentedAfter this update I am getting this error on every page:
PDOException: SQLSTATE[HY000] [2002] No such file or directory in lock_may_be_available() (line 167 of ../includes/lock.inc).
Comment #17
othermachines commentedHi, @drupalganech - By "this update" are you referring to the patch in this issue? Otherwise I don't think that it is related. Try: (Community Docs) PDOException: SQLSTATE[HY000] [2002] Can't connect to local MySQL server
Comment #18
Chris CharltonAnything holding #9 from being RTBC?
Comment #19
mcdruid commentedI don't think so :)
Comment #20
pmaruszczyk commentedThis path is not included in 7.64 version.
Comment #21
pmaruszczyk commentedComment #22
joseph.olstadComment #23
othermachines commentedPatch still applies cleanly.
Comment #24
joseph.olstadComment #25
mcdruid commentedComment #26
fabianx commentedWe talked about this:
- IS needs some better explanation
- I‘d like to understand the use case more if we need to do request sanitization before loading settings.php
Explicitly not setting to CNW as the code generally is fine
Comment #27
mcdruid commentedUpdated IS.
Comment #28
mcdruid commentedComment #29
mcdruid commentedComment #30
mcdruid commentedComment #31
mcdruid commentedComment #32
fabianx commentedRTBM - now all is clear! Thank you!
Over to mcdruid for commit.
Comment #34
mcdruid commentedThank you contributors!