Skip to content
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

Fixed #74063 - NumberFormatter did not work properly when restored from session. #2378

Closed
wants to merge 4 commits into from

Conversation

andrewnester
Copy link
Contributor

}

/* Create an ICU number formatter. */
FORMATTER_OBJECT(nfo) = unum_open(style, spattern, spattern_len, locale, NULL, &INTL_DATA_ERROR_CODE(nfo));
Copy link
Contributor

Choose a reason for hiding this comment

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

what if this fails?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The same call we have in numfmt_ctor and I guess in case if this call will fail, wrong number formatter object will be handled by FORMATTER_METHOD_FETCH_OBJECT macro

}

/* Create an ICU number formatter. */
FORMATTER_OBJECT(nfo) = unum_open(style, spattern, spattern_len, locale, NULL, &INTL_DATA_ERROR_CODE(nfo));
Copy link
Contributor

Choose a reason for hiding this comment

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

If pattern is not set or is not a string, this will be called with null pattern and probably will crash. Also, style may not be initialized when calling it. Also, I don't think default locale should be used when unserializing - this may lead to a situation where same object ins unserialized in different ways, leading to very hard to reproduce bugs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@smalyshev case when pattern is null covered with test provided, it works ok.

About style - agree, it's better at least to initialize it with 0

And good catch about default locate, thanks! I will fix it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@smalyshev just fixed it, thanks!

if (z_locale && Z_TYPE_P(z_locale) == IS_STRING) {
str = zval_get_string(z_locale);
locale = ZSTR_VAL(str);
zend_string_release(str);
Copy link
Member

@nikic nikic Feb 12, 2017

Choose a reason for hiding this comment

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

This releases the string to which locale still points (and is used later).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@nikic thanks for the review! just fixed all the issues.

str = zval_get_string(z_pattern);
pattern = ZSTR_VAL(str);
pattern_len = ZSTR_LEN(str);
zend_string_release(str);
Copy link
Member

Choose a reason for hiding this comment

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

Same here, this also releases a string that may later be used. Instead you should simply keep the whole zend_string and release it at the end.


if(spattern) {
efree(spattern);
}
Copy link
Member

Choose a reason for hiding this comment

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

Something went wrong with the indentation here, looks like mix of tabs and spaces.

@krakjoe
Copy link
Member

krakjoe commented Feb 13, 2017

@andrewnester I see commits yesterday, status please ?

@andrewnester
Copy link
Contributor Author

@krakjoe sorry, changes requested by @nikic fixed, mentioned it here #2378 (comment)


z_locale = zend_read_property(Z_OBJCE_P(return_value), return_value, "locale", sizeof("locale") - 1, 0, &rv);
if (z_locale != NULL) {
zstr_locale = zval_get_string(z_locale);
Copy link
Contributor

Choose a reason for hiding this comment

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

Since it's unserialized data, there's no guarantee z_locale is a string. It should be checked, and wakeup should fail if it's not.

Copy link
Member

Choose a reason for hiding this comment

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

This doesn't assume that z_locale is a string: It does a string cast.

Copy link
Contributor

@smalyshev smalyshev Feb 13, 2017

Choose a reason for hiding this comment

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

ah, well, I'm not sure it's the right thing to do anyway - what if it's an object? It's probably not safe to call __toString here... - but it's better then. For long conversion, it may be ok, not sure... I'd not risk it anyway and refuse to deal with broken data, we had enough problem with it lately.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree with @smalyshev - it would be better to check for types and fail waking up in case of wrong data

*/
PHP_METHOD( NumberFormatter, __wakeup )
{
const char* locale = intl_locale_get_default();
Copy link
Contributor

Choose a reason for hiding this comment

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

still uses default locale if serialization data do not contain locale value. Instead, it should fail.


z_style = zend_read_property(Z_OBJCE_P(return_value), return_value, "style", sizeof("style") - 1, 0, &rv);
if (z_style != NULL) {
style = zval_get_long(z_style);
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here, should not assume it's long.


z_pattern = zend_read_property(Z_OBJCE_P(return_value), return_value, "pattern", sizeof("pattern") - 1, 0, &rv);
if (z_pattern != NULL) {
zstr_pattern = zval_get_string(z_pattern);
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here, should not assume it's string.


/* Convert pattern (if specified) to UTF-16. */
if(pattern && pattern_len) {
intl_convert_utf8_to_utf16(&spattern, &spattern_len, pattern, pattern_len, &INTL_DATA_ERROR_CODE(nfo));
Copy link
Contributor

Choose a reason for hiding this comment

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

error code not checked here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@smalyshev thanks! just made changes you requested.

@nikic
Copy link
Member

nikic commented Feb 14, 2017

My main question here would be what our current stance on serialization of internal classes is in general. This particular PR uses an approach I haven't seen anywhere, which is to actually mirror the internal object state with exposed properties. The two approaches that are used elsewhere are a) using a get_properties handler to expose additional properties for serialization or b) use the Serializable interface. Both options are not idea, because get_properties leaves behind the additional properties, which confuses people (see ext/date), while Serializable always requires some custom code and some care to avoid security issues.

I'm wondering if it might make sense to add a get_properties_for_serialize handler (or similar), which is a variant of get_properties that is used for serialization (and returns a temporary HT). This would allow us to use wakeup-based unserialization without leaking additional (PHP-level) properties.

@smalyshev
Copy link
Contributor

I remember seeing this in other classes too, though don't remember the examples right now. In general, (un)serializing internal classes is a problem because not all classes have exportable - and, more importantly, importable - state. I think it makes a lot of sense to have centralized approach to this, and discussing it on internals and probably making the RFC looks like the right way to go here.

@andrewnester
Copy link
Contributor Author

@nikic @smalyshev just to let you know I took this approach from this https://github.com/php/php-src/blob/master/Zend/zend_exceptions.c#L314

@andrewnester
Copy link
Contributor Author

@nikic @smalyshev so what's the conclusion finally on this one?

@krakjoe
Copy link
Member

krakjoe commented Mar 11, 2017

@andrewnester it would seem there is some dissent from core maintainers about the suitability of this approach, and in addition, some suggestions for other solutions to the problem being solved here.

Since an RFC has been suggested, and since a discussion with the wider community would appear to be necessary, I would suggest at this point that you start an internals discussion with a view to creating an RFC, and possibly adopting a different approach.

Please link to any discussion here for future reference.

@andrewnester
Copy link
Contributor Author

@krakjoe thanks! I just started discussion here http://news.php.net/php.internals/98706

@andrewnester
Copy link
Contributor Author

@krakjoe I tried to start discussion on internals but doesn't look like it's going to happen

@krakjoe
Copy link
Member

krakjoe commented Jul 10, 2017

What I'm going to do is close this PR. What I suggest you do is open a new PR with a new approach, if we (those people that get involved in pull request discussions) find the approach to be acceptable, we may be able to skip the RFC, or it may just be a formality ... either way it would appear that this approach is dead in the water, and a fresh approach is needed.

I'm hoping we can skip an RFC if the approach is okay, personally. Sorry about the amount of time this is taking ...

@krakjoe krakjoe closed this Jul 10, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants