On Mon, Sep 15, 2014 at 2:47 PM, Anatol Belski <a...@php.net> wrote:

> On Mon, September 15, 2014 13:38, Anatol Belski wrote:
> > On Mon, September 15, 2014 13:13, Nikita Popov wrote:
> >
> >> On Mon, Sep 15, 2014 at 12:58 PM, Anatol Belski <a...@php.net> wrote:
> >>
> >>
> >>
> >>> Commit:    836fd73cce8d0550baf5477bfb0ea0edbfae455a
> >>> Author:    Anatol Belski <a...@php.net>         Mon, 15 Sep 2014
> >>> 12:12:18
> >>> +0200
> >>> Parents:   c8ed0d81e755c700f86cddb74f62eda27bc6e2de
> >>> Branches:  master
> >>>
> >>>
> >>>
> >>> Link:
> >>> http://git.php.net/?p=php-src.git;a=commitdiff;h=836fd73cce8d0550baf54
> >>> 77
> >>> bfb0ea0edbfae455a
> >>>
> >>> Log:
> >>> fix signed/unsigned mismatch
> >>>
> >>> Changed paths:
> >>> M  Zend/zend_execute.c
> >>>
> >>>
> >>>
> >>>
> >>> Diff:
> >>> diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index
> >>> 27636fd..5e774e2 100644
> >>> --- a/Zend/zend_execute.c
> >>> +++ b/Zend/zend_execute.c
> >>> @@ -771,7 +771,7 @@ static void zend_assign_to_string_offset(zval
> >>> *str,
> >>> zend_long offset, zval *valu }
> >>>
> >>>
> >>> old_str = Z_STR_P(str); -       if (offset >= Z_STRLEN_P(str)) { +
> >>> if (offset >= (zend_long)Z_STRLEN_P(str)) { zend_long old_len =
> >>> Z_STRLEN_P(str); Z_STR_P(str) =
> >>> zend_string_realloc(Z_STR_P(str), offset + 1, 0); Z_TYPE_INFO_P(str) =
> >>> IS_STRING_EX;
> >>> @@ -1226,7 +1226,7 @@ static zend_always_inline void
> >>> zend_fetch_dimension_address_read(zval *result, z offset =
> >>> Z_LVAL_P(dim);
> >>> }
> >>>
> >>>
> >>>
> >>> -               if (UNEXPECTED(offset < 0) ||
> >>> UNEXPECTED(Z_STRLEN_P(container) <= offset)) {
> >>> +               if (UNEXPECTED(offset < 0) ||
> >>> UNEXPECTED((zend_long)Z_STRLEN_P(container) <= offset)) {
> >>> if (type != BP_VAR_IS) { zend_error(E_NOTICE, "Uninitialized string
> >>> offset: %pd", offset);
> >>> }
> >>>
> >>>
> >>>
> >>
> >> I wonder if these casts shouldn't happen the other way around, i.e.
> >> whether offset should be cast to size_t instead of casting the length to
> >>  zend_long.
> >>
> >> The case I'm thinking about is this: If len > ZEND_LONG_MAX, then
> >> (zend_long)len will be < 0, so offset will always be >= than
> >> (zend_long)len
> >> in this case, leading to out of bounds memory access. If we cast the
> >> offset to (size_t) instead this shouldn't happen.
> >>
> > If offset is negative, there were an issue - thus the choice what to
> > cast. Probably we can do the offset sign check and handle accordingly,
> > that were future proof.
> >
> > I mean of course zend_string->len itself could exceed INT64_MAX, but not
> > the string itself. But that's an issue anyway which would have to be
> looked
> > up at some other place (like say somewhere would be done zend_string->len
> > = -1). Hm, an issue could be on 32 bit, if one allocates
> > a string not with str_repeat() but in the loop like while(1) {$s .= 'x';}
> > ... just that we wouldn't be able to pass an offset bigger that INT_MAX
> > anyway. Probably have to do more error logic.
> >
> The casts look safe for the normal case, memory_limit will not allow to
> exceed ZEND_LONG_MAX in PHP. So disregarding what HW does, looks like
> nothing to do :)
>
> Regards
>
> Anatol
>

Are you sure about this? I can set memory_limit to -1, which is then cast
to size_t, resulting in a limit > ZEND_LONG_MAX.

I tried the following in a 32bit VM with -dmemory_limit=-1 and got a
segfault:

<?php

$str = str_repeat('x', PHP_INT_MAX);
$str .= 'yyy';

$str[1] = 'a';

There is no crash if I cast offset to (size_t) instead.

Nikita

Reply via email to