Skip to content

Fix #81992: SplFixedArray::setSize() causes use-after-free #11959

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from

Conversation

nielsdos
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!

@@ -188,15 +189,15 @@ static void spl_fixedarray_resize(spl_fixedarray *array, zend_long size)
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.

@nielsdos
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.

@nielsdos
Copy link
Member Author

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

@nielsdos nielsdos 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