From e105186172a34f4c3ba05af594e34dfa63083b72 Mon Sep 17 00:00:00 2001 From: David Rothstein Date: Sat, 10 Oct 2015 11:40:33 -0400 Subject: Issue #496170 by stefan.r, chx, Fabianx, jbrauer, David_Rothstein, roderik, rwohleb, pounard, kenorb, Jose Reyero, joelpittet, catch: module_implements() cache can be polluted by module_invoke_all() being called (in)directly prior to full bootstrap completion --- CHANGELOG.txt | 3 +++ includes/common.inc | 5 ++++ includes/module.inc | 5 ++++ modules/simpletest/simpletest.info | 1 + modules/simpletest/tests/boot.test | 38 +++++++++++++++++++++++++++++ modules/simpletest/tests/boot_test_1.info | 6 +++++ modules/simpletest/tests/boot_test_1.module | 21 ++++++++++++++++ modules/simpletest/tests/boot_test_2.info | 6 +++++ modules/simpletest/tests/boot_test_2.module | 13 ++++++++++ 9 files changed, 98 insertions(+) create mode 100644 modules/simpletest/tests/boot.test create mode 100644 modules/simpletest/tests/boot_test_1.info create mode 100644 modules/simpletest/tests/boot_test_1.module create mode 100644 modules/simpletest/tests/boot_test_2.info create mode 100644 modules/simpletest/tests/boot_test_2.module diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 069595046..4e9e4f0da 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,6 +1,9 @@ Drupal 7.40, xxxx-xx-xx (development version) ----------------------- +- Fixed various bugs that occurred after hooks were invoked early in the Drupal + bootstrap and that caused module_implements() and drupal_alter() to cache an + incomplete set of hook implementations for later use. - Set the X-Content-Type-Options header to "nosniff" when possible, to prevent certain web browsers from picking an unsafe MIME type. - Prevented the database API from executing multiple queries at once on MySQL, diff --git a/includes/common.inc b/includes/common.inc index ceac115a5..ef71ee8b6 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -5212,6 +5212,11 @@ function _drupal_bootstrap_full() { fix_gpc_magic(); // Load all enabled modules module_load_all(); + // Reset drupal_alter() and module_implements() static caches as these + // include implementations for vital modules only when called early on + // in the bootstrap. + drupal_static_reset('drupal_alter'); + drupal_static_reset('module_implements'); // Make sure all stream wrappers are registered. file_get_stream_wrappers(); // Ensure mt_rand is reseeded, to prevent random values from one page load diff --git a/includes/module.inc b/includes/module.inc index 076992ca9..7bf619b47 100644 --- a/includes/module.inc +++ b/includes/module.inc @@ -867,6 +867,11 @@ function module_hook_info() { * @see module_implements() */ function module_implements_write_cache() { + // The list of implementations includes vital modules only before full + // bootstrap, so do not write cache if we are not fully bootstrapped yet. + if (drupal_get_bootstrap_phase() != DRUPAL_BOOTSTRAP_FULL) { + return; + } $implementations = &drupal_static('module_implements'); if (isset($implementations['#write_cache'])) { unset($implementations['#write_cache']); diff --git a/modules/simpletest/simpletest.info b/modules/simpletest/simpletest.info index 7b139ba3d..1aec619f5 100644 --- a/modules/simpletest/simpletest.info +++ b/modules/simpletest/simpletest.info @@ -11,6 +11,7 @@ configure = admin/config/development/testing/settings files[] = tests/actions.test files[] = tests/ajax.test files[] = tests/batch.test +files[] = tests/boot.test files[] = tests/bootstrap.test files[] = tests/cache.test files[] = tests/common.test diff --git a/modules/simpletest/tests/boot.test b/modules/simpletest/tests/boot.test new file mode 100644 index 000000000..562b082e8 --- /dev/null +++ b/modules/simpletest/tests/boot.test @@ -0,0 +1,38 @@ + 'Early bootstrap test', + 'description' => 'Confirm that calling module_implements() during early bootstrap does not pollute the module_implements() cache.', + 'group' => 'System', + ); + } + + function setUp() { + parent::setUp('boot_test_1', 'boot_test_2'); + } + + /** + * Test hook_boot() on both regular and "early exit" pages. + */ + public function testHookBoot() { + $paths = array('', 'early_exit'); + foreach ($paths as $path) { + // Empty the module_implements() caches. + module_implements(NULL, FALSE, TRUE); + // Do a request to the front page, which will call module_implements() + // during hook_boot(). + $this->drupalGet($path); + // Reset the static cache so we get implementation data from the persistent + // cache. + drupal_static_reset(); + // Make sure we get a full list of all modules implementing hook_help(). + $modules = module_implements('help'); + $this->assertTrue(in_array('boot_test_2', $modules)); + } + } +} diff --git a/modules/simpletest/tests/boot_test_1.info b/modules/simpletest/tests/boot_test_1.info new file mode 100644 index 000000000..a6f2ffb60 --- /dev/null +++ b/modules/simpletest/tests/boot_test_1.info @@ -0,0 +1,6 @@ +name = Early bootstrap tests +description = A support module for hook_boot testing. +core = 7.x +package = Testing +version = VERSION +hidden = TRUE diff --git a/modules/simpletest/tests/boot_test_1.module b/modules/simpletest/tests/boot_test_1.module new file mode 100644 index 000000000..a452e2897 --- /dev/null +++ b/modules/simpletest/tests/boot_test_1.module @@ -0,0 +1,21 @@ +