Make WordPress Core

Opened 3 years ago

Last modified 7 months ago

#52738 new defect (bug)

Use of get_object_vars() in sanitize_post() and WP_Post constructor does not handle null byte

Reported by: bitcomplex's profile bitcomplex Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version: 5.6.2
Component: Posts, Post Types Keywords: has-patch has-unit-tests needs-testing changes-requested
Focuses: Cc:

Description (last modified by SergeyBiryukov)

In places where get_object_vars is used to loop over an objects properties and then trying to access them null bytes are not handled.

There is an old bug-report (from me) for map_deep #47164 but now we are experience this in other places too; in sanitize_post and in the constructor of class-wp-post.

This is totally destroying our business and I don't know what to do. Since I reported the issue for map_deep I have had to manually patch formatting.php every time there is a WordPress update. But now, trying to handle all the places get_object_vars is used in hopeless.

Best approach to handle this would be to always filter the return values from get_object_vars. Something like:

<?php
    $properties = array_filter( fn( $var ) => ord( $var ) !== 0, get_object_vars( $object )); 

Change History (29)

#1 @SergeyBiryukov
19 months ago

#56690 was marked as a duplicate.

#2 @SergeyBiryukov
19 months ago

  • Component changed from General to Posts, Post Types
  • Description modified (diff)
  • Keywords needs-patch needs-unit-tests added
  • Milestone changed from Awaiting Review to 6.2
  • Summary changed from Use of get_object_vars does not handle null byte to Use of get_object_vars() in sanitize_post() and WP_Post constructor does not handle null byte

Hi there, welcome back to WordPress Trac!

Thanks for the ticket, sorry it took so long for someone to get back to you.

Moving to 6.2 along with #47164 to get more eyes on both tickets and hopefully resolve them.

#3 @cadic
18 months ago

I've performed a test across various core functions and was able to reproduce the issue with multiple approaches:

<?php
require_once ABSPATH . WPINC . '/class-wp-network.php';
require_once ABSPATH . WPINC . '/class-wp-site.php';

$test_array = array(
        'post_title' => 'Post Title',
        'post_type'  => 'page',
        "\0"         => 'Nullbyte',
);

$test_object = (object) $test_array;

/**
 * Each of these result in a Fatal Error:
 * Cannot access property starting with "\0"
 */
sanitize_post( $test_object );
new WP_Comment( $test_object );
new WP_Network( $test_object );
new WP_Post( $test_object );
new WP_Term( $test_object );
map_deep( $test_object, 'absint' );
new WP_Site( $test_object );

#4 @cadic
18 months ago

Could be solved by replacing get_object_vars() with a wrapper function

<?php
function wp_get_object_vars( $object ) {
        return array_filter(
                get_object_vars( $object ),
                function( $key ) {
                        return ord( $key ) !== 0;
                },
                ARRAY_FILTER_USE_KEY
        );
}
Last edited 18 months ago by cadic (previous) (diff)

This ticket was mentioned in PR #3607 on WordPress/wordpress-develop by @cadic.


18 months ago
#5

  • Keywords has-patch has-unit-tests added; needs-patch needs-unit-tests removed

This ticket was mentioned in Slack in #core by mukeshpanchal27. View the logs.


14 months ago

#7 @mukesh27
14 months ago

  • Keywords needs-testing added

This ticket was discussed during the recent bug scrub. It looks like it's unlikely that work will be done on this during the 6.2 cycle.

needs-testing added as PR need some testing.

This ticket was mentioned in Slack in #core-test by robinwpdeveloper. View the logs.


14 months ago

This ticket was mentioned in Slack in #core by costdev. View the logs.


14 months ago

#10 @costdev
14 months ago

  • Milestone changed from 6.2 to 6.3
  • Severity changed from critical to normal

This ticket was discussed in the bug scrub and it was agreed to move this ticket to 6.3 and to reduce the severity to normal. Hopefully we'll land this one next cycle.

This ticket was mentioned in Slack in #core by oglekler. View the logs.


11 months ago

#12 @oglekler
10 months ago

@costdev @audrasjb @SergeyBiryukov @mukesh27, sorry for pinging you all, but can you place look at this patch. The new function looks logical and in WordPress tradition to replace PHP functions with owns to make them more predictable with result, it is covered with unit tests; get_object_vars() isn't replaced for the new wp_get_object_vars() everywhere, but perhaps it is not needed.

@bitcomplex, what is the scenario when get_object_vars() gets an object which has null as a property? It looks like a mistake from some other place we are covering for, but fatal error is not pleasant.

I am also wondering if we need to add an error message to the debug log in cases when it is actually happening to make this easier to debug for developers who are making such a mistake.

#13 follow-up: @costdev
10 months ago

Thanks for the ping @oglekler! I meant to get a closer look at this sooner but only ever had a cursory look during scrubs or when taking a look at test failures on the map_deep() ticket.

Sergey was able to reproduce the issue on the map_deep() ticket, and here's a 3v4l that might help to visualise the issue and the proposed patch.

My thoughts on whether to patch this:

  • Is the value containing NUL bytes produced by Core?
    • If the answer is no, then this is an enhancement request to add support for NUL bytes, not a defect (bug) report, and should be moved out of the 6.3 milestone as we're now in Beta. (Note: A Fatal Error doesn't necessarily mean a bug in Core, it could just mean that PHP says "no" to something extenders are trying to do with Core)
  • Is the use case valid?
    • If so, we should continue.
    • If not, we should consider closing this as invalid as the issue should be rectified where the value is generated and/or stored.

Without more information, it's hard to classify this ticket before deciding the right course of action. I'd suggest adding reporter-feedback so we have the information necessary to move forward.

#14 in reply to: ↑ 13 @bitcomplex
10 months ago

As the reporter of the bug; I'm still convinced this is a serious bug in core. It still affect us every release and we have to patch it manually.

The real issue is when you serialize objects and later change the visibility of a property in the class the object belongs too. Since you've decided that it is a good idea to store serialized objects you should also handle changes of classes in a way that do not cause fatals.

It is impossible to fix the old objects that are permanently stored in the database, but fixing this by ignoring null bytes upon reading, handles the issue perfectly.

Replying to costdev:

Thanks for the ping @oglekler! I meant to get a closer look at this sooner but only ever had a cursory look during scrubs or when taking a look at test failures on the map_deep() ticket.

Sergey was able to reproduce the issue on the map_deep() ticket, and here's a 3v4l that might help to visualise the issue and the proposed patch.

My thoughts on whether to patch this:

  • Is the value containing NUL bytes produced by Core?
    • If the answer is no, then this is an enhancement request to add support for NUL bytes, not a defect (bug) report, and should be moved out of the 6.3 milestone as we're now in Beta. (Note: A Fatal Error doesn't necessarily mean a bug in Core, it could just mean that PHP says "no" to something extenders are trying to do with Core)
  • Is the use case valid?
    • If so, we should continue.
    • If not, we should consider closing this as invalid as the issue should be rectified where the value is generated and/or stored.

Without more information, it's hard to classify this ticket before deciding the right course of action. I'd suggest adding reporter-feedback so we have the information necessary to move forward.

#15 @oglekler
10 months ago

@bitcomplex, where do these objects with broken properties are coming from? If WordPress, or plugin or theme is changing data structure, there is a migration in place which checks the current version stored in the DB and compares it with the one it needs and if it is not the same runs a migration. So, data is transformed once, new tables created etc. whatever it needs and then a new version is stored in the options to indicate current state and there are no more problems.

#16 @bitcomplex
10 months ago

@oglekler our own plugin, where some old classes (which is core business logic) are stored as meta data to posts.

I'm 100% sure that we havn't followed any WordPress migration principles for the last 5 years or so, but the point is that you should not have a system that can throw fatals where there is even a theoretical chance of it happening in the wild.

To me WordPress is just broken. Since the stored data is permanent it is enough to forget following any wp guidelines even once to have the object always breaking everything if it loads.


Last edited 10 months ago by bitcomplex (previous) (diff)

#17 @costdev
10 months ago

  • Keywords changes-requested added

Certainly, Core's implementation in map_deep(), for example, doesn't take an (object) (array) $object scenario into account, nor does its documentation exclude that scenario. In that regard, we could do with improving the implementation to account for this.

While introducing and implementing wp_get_object_vars() should cover that scenario, I'm aware that this also enables extenders to forego good practice of following migration principles.

I've left a review on PR 3607 to tidy things up and meet Core standards/convention should it get support for inclusion in Core. Adding changes-requested to indicate the current status of the patch.

If it does get support once additional Core developers have weighed in, then as @oglekler mentions, there are other instances of get_object_vars() in Core that haven't been changed to use wp_get_object_vars(). As the PR adds tests for each of the changed calls, we may either want to do everything at once, or follow up with additional patches to tackle the rest where applicable.

Last edited 10 months ago by costdev (previous) (diff)

#18 follow-up: @costdev
10 months ago

@bitcomplex The real issue is when you serialize objects and later change the visibility of a property in the class the object belongs too. Since you've decided that it is a good idea to store serialized objects you should also handle changes of classes in a way that do not cause fatals.

While there's room for improvement in handling cases such as (object) (array) $object, can you clarify who "you" refers to in each of these so that it's clear to myself and others?

  1. [you] serialize objects and [you] later change the visibility of a property in the class the object belongs too
  2. [you've] decided that it is a good idea to store serialized objects
  3. [you] should also handle changes of classes in a way that do not cause fatals

Where "you" refers to Core doing something, can you also provide more information about when Core does this? Thanks!

Last edited 10 months ago by costdev (previous) (diff)

This ticket was mentioned in Slack in #core by mukeshpanchal27. View the logs.


10 months ago

This ticket was mentioned in Slack in #core by chaion07. View the logs.


9 months ago

This ticket was mentioned in Slack in #core by chaion07. View the logs.


9 months ago

#22 @chaion07
9 months ago

  • Milestone changed from 6.3 to 6.4

Thanks @bitcomplex for reporting this. We reviewed this ticket during a recent bug-scrub session and based on the feedback we are updating the milestone to 6.4. Cheers!

Props to @mukesh27

This ticket was mentioned in Slack in #core by marybaum. View the logs.


8 months ago

#24 @marybaum
8 months ago

I'll test the patch this week and see what heeds to happen afterward.

#25 follow-up: @marybaum
8 months ago

@beapi will test the patch, pending some information from @bitcomplex. That supersedes my needing to test the patch. Thanks, Clement!

#26 in reply to: ↑ 25 @bitcomplex
7 months ago

Replying to marybaum:

@beapi will test the patch, pending some information from @bitcomplex. That supersedes my needing to test the patch. Thanks, Clement!

What information do you need from me?

This ticket was mentioned in Slack in #core by marybaum. View the logs.


7 months ago

#28 @costdev
7 months ago

  • Milestone changed from 6.4 to Awaiting Review

This ticket was discussed in the bug scrub. It was agreed to move this back to Awaiting Review until we can determine whether this is a bug in Core, a situation of incorrect usage, or an enhancement request. The ticket can be milestoned if this is established and becomes actionable.

Per a suggestion in the bug scrub, pinging @bitcomplex to take a look back at this comment and respond.

#29 in reply to: ↑ 18 @bitcomplex
7 months ago

Replying to costdev:

While there's room for improvement in handling cases such as (object) (array) $object, can you clarify who "you" refers to in each of these so that it's clear to myself and others?

  1. [you] serialize objects and [you] later change the visibility of a property in the class the object belongs too
  1. WordPress serialize objects and 3rd party later change the visibility of a property in the class the object belongs too
  1. [you've] decided that it is a good idea to store serialized objects
  1. WordPress developers have decided that it is a good idea to store serialized objects
  1. [you] should also handle changes of classes in a way that do not cause fatals
  1. WordPress developers should also handle changes of classes in a way that do not cause fatals. (This does not absolve 3rd party developers from not following WP-guidelines. But not following guidlines should not cause fatals).
Note: See TracTickets for help on using tickets.