Make WordPress Core

Changeset 56796


Ignore:
Timestamp:
10/06/2023 04:31:22 PM (14 months ago)
Author:
flixos90
Message:

Options, Meta APIs: Fix bug with update_option() updating the wrong cache, leading to potentially stale values being returned.

When using the $autoload parameter of update_option() alongside an option value update, prior to this changeset the function would update the incorrect cache, not respecting the new autoload value. This could have severe implications such as returning a stale option value when the option in fact had already been deleted.

This changeset fixes the bug alongside test coverage that failed with trunk but now passes.

Props kkmuffme, pentatonicfunk, SergeyBiryukov, oglekler, azaozz, spacedmonkey, nicolefurlan, joemcgill, flixos90.
Fixes #51352.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/option.php

    r56788 r56796  
    860860
    861861    if ( ! wp_installing() ) {
    862         $alloptions = wp_load_alloptions( true );
    863         if ( isset( $alloptions[ $option ] ) ) {
     862        if ( ! isset( $update_args['autoload'] ) ) {
     863            // Update the cached value based on where it is currently cached.
     864            $alloptions = wp_load_alloptions( true );
     865            if ( isset( $alloptions[ $option ] ) ) {
     866                $alloptions[ $option ] = $serialized_value;
     867                wp_cache_set( 'alloptions', $alloptions, 'options' );
     868            } else {
     869                wp_cache_set( $option, $serialized_value, 'options' );
     870            }
     871        } elseif ( 'yes' === $update_args['autoload'] ) {
     872            // Delete the individual cache, then set in alloptions cache.
     873            wp_cache_delete( $option, 'options' );
     874
     875            $alloptions = wp_load_alloptions( true );
    864876            $alloptions[ $option ] = $serialized_value;
    865877            wp_cache_set( 'alloptions', $alloptions, 'options' );
    866878        } else {
     879            // Delete the alloptions cache, then set the individual cache.
     880            $alloptions = wp_load_alloptions( true );
     881            if ( isset( $alloptions[ $option ] ) ) {
     882                unset( $alloptions[ $option ] );
     883                wp_cache_set( 'alloptions', $alloptions, 'options' );
     884            }
     885
    867886            wp_cache_set( $option, $serialized_value, 'options' );
    868887        }
  • trunk/tests/phpunit/tests/option/option.php

    r56788 r56796  
    778778        $this->assertSame( 10, has_filter( 'pre_option_foo', '__return_zero' ) );
    779779    }
     780
     781    /**
     782     * Tests that calling update_option() with changed autoload from 'no' to 'yes' updates the cache correctly.
     783     *
     784     * This ensures that no stale data is served in case the option is deleted after.
     785     *
     786     * @ticket 51352
     787     *
     788     * @covers ::update_option
     789     */
     790    public function test_update_option_with_autoload_change_no_to_yes() {
     791        add_option( 'foo', 'value1', '', 'no' );
     792        update_option( 'foo', 'value2', 'yes' );
     793        delete_option( 'foo' );
     794        $this->assertFalse( get_option( 'foo' ) );
     795    }
     796
     797    /**
     798     * Tests that calling update_option() with changed autoload from 'yes' to 'no' updates the cache correctly.
     799     *
     800     * This ensures that no stale data is served in case the option is deleted after.
     801     *
     802     * @ticket 51352
     803     *
     804     * @covers ::update_option
     805     */
     806    public function test_update_option_with_autoload_change_yes_to_no() {
     807        add_option( 'foo', 'value1', '', 'yes' );
     808        update_option( 'foo', 'value2', 'no' );
     809        delete_option( 'foo' );
     810        $this->assertFalse( get_option( 'foo' ) );
     811    }
    780812}
Note: See TracChangeset for help on using the changeset viewer.