[wp-trac] [WordPress Trac] #53986: Problematic error-handling in sanitize_option()

WordPress Trac noreply at wordpress.org
Mon Aug 23 21:59:35 UTC 2021


#53986: Problematic error-handling in sanitize_option()
----------------------------------------+---------------------
 Reporter:  iCaleb                      |       Owner:  (none)
     Type:  defect (bug)                |      Status:  new
 Priority:  normal                      |   Milestone:  5.9
Component:  Options, Meta APIs          |     Version:  trunk
 Severity:  normal                      |  Resolution:
 Keywords:  has-patch needs-unit-tests  |     Focuses:
----------------------------------------+---------------------
Changes (by SergeyBiryukov):

 * keywords:  has-patch => has-patch needs-unit-tests
 * milestone:  Awaiting Review => 5.9


Old description:

> Given a very unfortunate series of events, it's possible for a site to
> brick itself from a call to `update_option()` on even a FE request that
> should have just resulted in no update (option value was not changing).
> Introduced in https://core.trac.wordpress.org/ticket/32350
>
> == What happens
>
> 1. Error is thrown here due to a query failure:
> https://github.com/WordPress/WordPress/blob/270f2011f8ec7265c3f4ddce39c77ef5b496ed1c
> /wp-includes/wp-db.php#L2776
> 2. Error is eventually returned here for say `category_base`:
> https://github.com/WordPress/WordPress/blob/b60a512de42c0a2ce2fa258e9be70bedb29b337c
> /wp-includes/formatting.php#L4853-L4855
> 3. But this fails to catch the error:
> https://github.com/WordPress/WordPress/blob/b60a512de42c0a2ce2fa258e9be70bedb29b337c
> /wp-includes/formatting.php#L4890
>
> Why you ask?
>
> {{{
> $value = new WP_Error( 'wpdb_get_table_charset_failure' );
> $error = $value->get_error_message();
> => string(0) ""
> }}}
>
> == Replicating
>
> Replicating the true process of how things go badly is not very easy, as
> you need certain queries to pass along the way and very specific ones to
> fail. But an easier replication can be done like so:
>
> {{{
> wp shell
>
> $option_value = new WP_Error( 'wpdb_get_table_charset_failure' );
> update_option( 'category_base', $option_value );
> }}}
>
> Note that this will immediately brick the site. To cleanup, will need to
> delete the options from the database manually:
>
> {{{
> delete from wp_options where option_name = 'rewrite_rules';
> delete from wp_options where option_name = 'category_base';
> }}}
>
> And if using object cache, need to clear that out as well. Comment out
> this line
> https://github.com/WordPress/WordPress/blob/94c655c89286fdf43e5bab82d4108fb679e46a7a
> /wp-includes/default-filters.php#L580, and `wp cache flush`.
>
> == Resolution
>
> Patch incoming, but defaulting the $error to `null` in sanitize_option()
> and checking against that specifically will help future-proof this. And
> then to be more descriptive in the error handling, can also add error
> descriptions to `WP_Error`'s where needed.

New description:

 Given a very unfortunate series of events, it's possible for a site to
 brick itself from a call to `update_option()` on even a FE request that
 should have just resulted in no update (option value was not changing).
 Introduced in #32350.

 == What happens

 1. Error is thrown here due to a query failure:
 https://github.com/WordPress/WordPress/blob/270f2011f8ec7265c3f4ddce39c77ef5b496ed1c
 /wp-includes/wp-db.php#L2776
 2. Error is eventually returned here for say `category_base`:
 https://github.com/WordPress/WordPress/blob/b60a512de42c0a2ce2fa258e9be70bedb29b337c
 /wp-includes/formatting.php#L4853-L4855
 3. But this fails to catch the error:
 https://github.com/WordPress/WordPress/blob/b60a512de42c0a2ce2fa258e9be70bedb29b337c
 /wp-includes/formatting.php#L4890

 Why you ask?

 {{{
 $value = new WP_Error( 'wpdb_get_table_charset_failure' );
 $error = $value->get_error_message();
 => string(0) ""
 }}}

 == Replicating

 Replicating the true process of how things go badly is not very easy, as
 you need certain queries to pass along the way and very specific ones to
 fail. But an easier replication can be done like so:

 {{{
 wp shell

 $option_value = new WP_Error( 'wpdb_get_table_charset_failure' );
 update_option( 'category_base', $option_value );
 }}}

 Note that this will immediately brick the site. To cleanup, will need to
 delete the options from the database manually:

 {{{
 delete from wp_options where option_name = 'rewrite_rules';
 delete from wp_options where option_name = 'category_base';
 }}}

 And if using object cache, need to clear that out as well. Comment out
 this line
 https://github.com/WordPress/WordPress/blob/94c655c89286fdf43e5bab82d4108fb679e46a7a
 /wp-includes/default-filters.php#L580, and `wp cache flush`.

 == Resolution

 Patch incoming, but defaulting the $error to `null` in sanitize_option()
 and checking against that specifically will help future-proof this. And
 then to be more descriptive in the error handling, can also add error
 descriptions to `WP_Error`'s where needed.

--

Comment:

 Hi there, thanks for the ticket and the PR!

 This looks good to me at a glance. Would be great to also add a unit test
 for this.

-- 
Ticket URL: <https://core.trac.wordpress.org/ticket/53986#comment:2>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list