Skip to content

Conversation

@ndossche
Copy link
Member

Upon resizing, the elements are destroyed from lower index to higher index. When an element contains an object with a destructor, that destructor can access a lower (i.e. already destroyed) element, causing a uaf. Set refcounted zvals to NULL after destroying them to avoid a uaf.

Alternative solution I thought about: Maybe it's possible to do it in two passes. First run all destructors and then destroy everything. But I don't know off the top of my head if the engine supports that (in an easy way). It seems complicated, and would also be technically a BC break because the destructors see still alive data that they might've expected to be destroyed.

The solution in this PR is BC-preserving because it previously just crashed instead of doing anything useful.

Upon resizing, the elements are destroyed from lower index to higher
index. When an element refers to an object with a destructor, it can
refer to a lower (i.e. already destroyed) element, causing a uaf.
Set refcounted zvals to NULL after destroying them to avoid a uaf.
Copy link
Member

@iluuu1994 iluuu1994 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice solution!

if (size == 0) {
spl_fixedarray_dtor(array);
array->elements = NULL;
array->size = 0;
Copy link
Member

@iluuu1994 iluuu1994 Aug 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be set before spl_fixedarray_dtor to avoid the same issue on deallocation of the whole array? Edit: Ah, we're using size in there. Thinking about it, we might even do other shenanigans like modify the array in the destructor of released objects...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll have a closer look, I'm currently fixing the case of calling resize within resize, still causing uaf :/ I think I have a solution for that though.
I'll have a deeper look at your comment after that.

@ndossche
Copy link
Member Author

I've pushed a working fix for the resize UAF.
We don't want to take a copy of the elements array just in case, because of the double memory consumption during the resizing.

My solution is the following:
When a resize happens whilst resizing, don't perform the nested resize. Instead, wait until all elements of the currently executed resize are destroyed, and only then perform the second resize.
When there are multiple nested resizes, only perform the last one.

Copy link
Member

@iluuu1994 iluuu1994 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, I tried to abuse it in various ways but was unable to do so.

@ndossche
Copy link
Member Author

@iluuu1994 as usual, thanks a lot for the sanity check ^^ !

@ndossche ndossche closed this in b71c6b2 Aug 14, 2023
jorgsowa pushed a commit to jorgsowa/php-src that referenced this pull request Aug 16, 2023
Upon resizing, the elements are destroyed from lower index to higher
index. When an element refers to an object with a destructor, it can
refer to a lower (i.e. already destroyed) element, causing a uaf.
Set refcounted zvals to NULL after destroying them to avoid a uaf.

Closes phpGH-11959.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants