#40749 [NEW]: pack and unpack erroneous behavior on 64bits hosts

Ask a Question related to PHP Bugs, Design and Development.

  1. #1

    Default #40749 [NEW]: pack and unpack erroneous behavior on 64bits hosts

    From: ben at ateor dot com
    Operating system: OpenBSD amd64 and sparc64
    PHP version: 5.2.1
    PHP Bug Type: Reproducible crash
    Bug description: pack and unpack erroneous behavior on 64bits hosts

    Description:
    ------------
    This is a follow-up on #40543 ([url]http://bugs.php.net/bug.php?id=40543[/url],
    since
    that bug is closed, I can't add comments).
    Please note : it's not identical to #4053 (other weird behaviors
    are demonstrated).

    Iliaa,
    Not sure why you suggest to use little endian or host conversions
    routines,
    but in my standpoint if you reverse twice a number's byte ordering
    then you should get the original number back (assuming the number don't
    overflows php's internals).

    At least, that's the standard behavior for perl and python.

    Beside, I can't see why php should handles those endianness conversions
    differently on an i386 (32 bits) and on an x86_64 (64 bits), both
    having the same byte order.

    The following on a 64bit, little endian host :
    x86_64$ uname -mprsv
    OpenBSD 4.0 GENERIC#0 amd64 AMD Sempron(tm) Processor 3400+

    x86_64$ perl -e 'print unpack("N", pack("N", 41445)) ."\n"'
    41445

    x86_64$ python
    Python 2.4.3 (#1, Sep 6 2006, 20:33:08)
    [GCC 3.3.5 (propolice)] on openbsd4
    Type "help", "copyright", "credits" or "license" for more information.
    >>> from struct import *
    >>> unpack('>L', pack('>L', 41445))
    (41445L,)

    And, indeed :
    #include <stdio.h>
    #include <sys/types.h>
    int main(void)
    {
    u_int32_t x, y, z; /* 32 bits unsigned longs */
    x = 41445;
    /* conv host (little) to network (big endian) long : pack("N",
    41445) */
    y = htonl(x);
    /* conv network (big endian) to host (little) long : unpack("N",
    ....) */
    z = ntohl(y);
    printf("Host : %li\nBig : %li\nHost : %li\n", x, y, z);
    return 0;
    }

    x86_64$ gcc conv.c -o conv ; ./conv
    Host : 41445
    Big : -442433536
    Host : 41445

    But still (PHP 5.2.2-dev (cli) (built: Feb 27 2007 22:10:11)) :
    x86_64$ php -r 'print_r(unpack("N", pack("N", 41445)));'
    Array
    (
    [1] => -2147442203
    )

    While on a plain old x86 little endian host (PHP 4.4.0), we get
    a different result :
    i386_32$ uname -mprsv
    OpenBSD 3.9 GENERIC#0 i386 Intel(R) Pentium(R) 4 CPU 1.70GHz
    ("GenuineIntel" 686-class)
    i386_32$ php -r 'print_r(unpack("N", pack("N", 41445)));'
    Array
    (
    [1] => 41445
    )

    Still on the 64 bits little endian host :
    x86_64$ php -r '$a = unpack("N", pack("N", 65536)); printf("$a[1]\n");'
    65536 # Ok
    x86_64$ php -r '$a = unpack("N", pack("N", 65535)); printf("$a[1]\n");'
    -2147418113 # Weird

    x86_64$ php -r '$a = unpack("N", pack("N", 0x1234)); printf("0x%x\n",
    $a[1]);'
    0x1234 # Ok
    x86_64$ php -r '$a = unpack("L", pack("N", 0x1234)); printf("0x%x\n",
    $a[1]);'
    0x34120000 # Ok
    x86_64$ php -r '$a = unpack("L", pack("L", 0xffff)); printf("0x%x\n",
    $a[1]);'
    0xffff # Ok
    x86_64$ php -r '$a = unpack("N", pack("N", 0xffff)); printf("0x%x\n",
    $a[1]);'
    0xffffffff8000ffff # The doc says "N" gives you "always 32 bit", and we
    get
    # 8 bytes. No wonder why we overflow.
    x86_64$ php -r '$a = unpack("N", pack("N", 0xff )); printf("0x%x\n",
    $a[1]);'
    0xffffffff800000ff # Same. Don't tell me 0xff is too large.

    And now, all the following tests are on a 64 bits _big endian_ host
    (sparc64, running php-5.2.1) :
    sparc64$ uname -mprsv
    OpenBSD 3.8 GENERIC#607 sparc64 SUNW,UltraSPARC-IIi @ 440 MHz, version 0
    FPU
    sparc64$ php -r '$a = unpack("N", pack("N", 0xffff)); printf("0x%x\n",
    $a[1]);'
    0xffff # Ok
    # The same, but prefixing 0000 to the argument :
    sparc64$ php -r '$a = unpack("N", pack("N", 0x0000ffff)); printf("0x%x\n",
    $a[1]);'
    0xffffffff8000ffff
    # Weird (and with "N", we stayed on the host byte order this time).
    # Shouldn't 0xffff == 0x0000ffff, even on big endian ? Apparently, yes :
    sparc64$ php -r 'printf("0x%x\n", 0x0000ffff);'
    0xffff

    # Also, look at this :
    sparc64$ php -r '$a = unpack("N", pack("N", 41445)); printf("$a[1]\n");'
    41445
    # And now let's just remove the line feed (\n) from the above printf :
    sparc64$ php -r '$a = unpack("N", pack("N", 41445)); printf("$a[1]");'
    -2147442203

    # Same for 2^16 -1 / 65535 / 0xfff :
    sparc64$ php -r '$a = unpack("N", pack("N", 65535)); printf("$a[1]\n");'
    65535
    sparc64$ php -r '$a = unpack("N", pack("N", 65535)); printf("$a[1]");'
    -2147418113

    # We get the opposite (bogus with \n, correct without) when converting
    # to little endian and back to host :
    sparc64$ php -r '$a = unpack("L", pack("L", 0xffff)); printf( $a[1]);'
    65535
    sparc64$ php -r '$a = unpack("L", pack("L", 0xffff)); printf(
    $a[1]."\n");'
    -2147418113


    This doesn't help :
    SKIP Generic pack()/unpack() tests [ext/standard/tests/strings/pack.phpt]
    reason: 32bit test only




    --
    Edit bug report at [url]http://bugs.php.net/?id=40749&edit=1[/url]
    --
    Try a CVS snapshot (PHP 4.4): [url]http://bugs.php.net/fix.php?id=40749&r=trysnapshot44[/url]
    Try a CVS snapshot (PHP 5.2): [url]http://bugs.php.net/fix.php?id=40749&r=trysnapshot52[/url]
    Try a CVS snapshot (PHP 6.0): [url]http://bugs.php.net/fix.php?id=40749&r=trysnapshot60[/url]
    Fixed in CVS: [url]http://bugs.php.net/fix.php?id=40749&r=fixedcvs[/url]
    Fixed in release: [url]http://bugs.php.net/fix.php?id=40749&r=alreadyfixed[/url]
    Need backtrace: [url]http://bugs.php.net/fix.php?id=40749&r=needtrace[/url]
    Need Reproduce Script: [url]http://bugs.php.net/fix.php?id=40749&r=needscript[/url]
    Try newer version: [url]http://bugs.php.net/fix.php?id=40749&r=oldversion[/url]
    Not developer issue: [url]http://bugs.php.net/fix.php?id=40749&r=support[/url]
    Expected behavior: [url]http://bugs.php.net/fix.php?id=40749&r=notwrong[/url]
    Not enough info: [url]http://bugs.php.net/fix.php?id=40749&r=notenoughinfo[/url]
    Submitted twice: [url]http://bugs.php.net/fix.php?id=40749&r=submittedtwice[/url]
    register_globals: [url]http://bugs.php.net/fix.php?id=40749&r=globals[/url]
    PHP 3 support discontinued: [url]http://bugs.php.net/fix.php?id=40749&r=php3[/url]
    Daylight Savings: [url]http://bugs.php.net/fix.php?id=40749&r=dst[/url]
    IIS Stability: [url]http://bugs.php.net/fix.php?id=40749&r=isapi[/url]
    Install GNU Sed: [url]http://bugs.php.net/fix.php?id=40749&r=gnused[/url]
    Floating point limitations: [url]http://bugs.php.net/fix.php?id=40749&r=float[/url]
    No Zend Extensions: [url]http://bugs.php.net/fix.php?id=40749&r=nozend[/url]
    MySQL Configuration Error: [url]http://bugs.php.net/fix.php?id=40749&r=mysqlcfg[/url]
    ben at ateor dot com Guest

  2. Similar Questions and Discussions

    1. #40543 [NEW]: pack/unpack bug
      From: dedmajor at gmail dot com Operating system: linux x86_64 PHP version: 5.2.1 PHP Bug Type: Unknown/Other Function Bug...
    2. #40562 [NEW]: pack/unpack still broken on 64bit
      From: windeler at mediafinanz dot de Operating system: Linux x86_64Bit PHP version: 5.2.1 PHP Bug Type: Unknown/Other...
    3. #38770 [NEW]: pack/unpack is broken on 64bit
      From: fwmatt at dsl dot pipex dot com Operating system: Linux x86_64 PHP version: 5.1.6 PHP Bug Type: Unknown/Other Function...
    4. pack/unpack bits
      I have a problem with using unpack to separate out some arbitrary bits from some binary data received using some wacky protocol. The first bit of...
    5. pack and unpack question for perl 5.8.0
      My old perl code has the following that works in 5.6.0: while($line = <STDIN>){ chomp $line; $aline = $line; ($tline = $aline) =~...
  3. #2

    Default #40749 [Com]: pack and unpack erroneous behavior on 64bits hosts

    ID: 40749
    Comment by: windeler at mediafinanz dot de
    Reported By: ben at ateor dot com
    Status: Open
    Bug Type: Unknown/Other Function
    Operating System: OpenBSD amd64 and sparc64
    PHP Version: 5.2.1
    New Comment:

    Here is another example of a problem with unpack on 64bit systems. It
    worked in 5.1.6, but with 5.2.1 the results are bogus.

    The expected value from the file content is 200, but PHP says
    -2147483448 when I echo $a['i'].

    <?php
    $f = fopen('test.pdf','rb');
    //Read a 4-byte integer from file
    $a = unpack('Ni',fread($f,4));
    echo $a['i'];
    fclose($f);
    ?>


    Previous Comments:
    ------------------------------------------------------------------------

    [2007-03-07 17:12:58] ben at ateor dot com

    Description:
    ------------
    This is a follow-up on #40543 ([url]http://bugs.php.net/bug.php?id=40543[/url],
    since
    that bug is closed, I can't add comments).
    Please note : it's not identical to #4053 (other weird behaviors
    are demonstrated).

    Iliaa,
    Not sure why you suggest to use little endian or host conversions
    routines,
    but in my standpoint if you reverse twice a number's byte ordering
    then you should get the original number back (assuming the number
    don't
    overflows php's internals).

    At least, that's the standard behavior for perl and python.

    Beside, I can't see why php should handles those endianness
    conversions
    differently on an i386 (32 bits) and on an x86_64 (64 bits), both
    having the same byte order.

    The following on a 64bit, little endian host :
    x86_64$ uname -mprsv
    OpenBSD 4.0 GENERIC#0 amd64 AMD Sempron(tm) Processor 3400+

    x86_64$ perl -e 'print unpack("N", pack("N", 41445)) ."\n"'
    41445

    x86_64$ python
    Python 2.4.3 (#1, Sep 6 2006, 20:33:08)
    [GCC 3.3.5 (propolice)] on openbsd4
    Type "help", "copyright", "credits" or "license" for more information.
    >>> from struct import *
    >>> unpack('>L', pack('>L', 41445))
    (41445L,)

    And, indeed :
    #include <stdio.h>
    #include <sys/types.h>
    int main(void)
    {
    u_int32_t x, y, z; /* 32 bits unsigned longs */
    x = 41445;
    /* conv host (little) to network (big endian) long : pack("N",
    41445) */
    y = htonl(x);
    /* conv network (big endian) to host (little) long :
    unpack("N", ...) */
    z = ntohl(y);
    printf("Host : %li\nBig : %li\nHost : %li\n", x, y, z);
    return 0;
    }

    x86_64$ gcc conv.c -o conv ; ./conv
    Host : 41445
    Big : -442433536
    Host : 41445

    But still (PHP 5.2.2-dev (cli) (built: Feb 27 2007 22:10:11)) :
    x86_64$ php -r 'print_r(unpack("N", pack("N", 41445)));'
    Array
    (
    [1] => -2147442203
    )

    While on a plain old x86 little endian host (PHP 4.4.0), we get
    a different result :
    i386_32$ uname -mprsv
    OpenBSD 3.9 GENERIC#0 i386 Intel(R) Pentium(R) 4 CPU 1.70GHz
    ("GenuineIntel" 686-class)
    i386_32$ php -r 'print_r(unpack("N", pack("N", 41445)));'
    Array
    (
    [1] => 41445
    )

    Still on the 64 bits little endian host :
    x86_64$ php -r '$a = unpack("N", pack("N", 65536));
    printf("$a[1]\n");'
    65536 # Ok
    x86_64$ php -r '$a = unpack("N", pack("N", 65535));
    printf("$a[1]\n");'
    -2147418113 # Weird

    x86_64$ php -r '$a = unpack("N", pack("N", 0x1234)); printf("0x%x\n",
    $a[1]);'
    0x1234 # Ok
    x86_64$ php -r '$a = unpack("L", pack("N", 0x1234)); printf("0x%x\n",
    $a[1]);'
    0x34120000 # Ok
    x86_64$ php -r '$a = unpack("L", pack("L", 0xffff)); printf("0x%x\n",
    $a[1]);'
    0xffff # Ok
    x86_64$ php -r '$a = unpack("N", pack("N", 0xffff)); printf("0x%x\n",
    $a[1]);'
    0xffffffff8000ffff # The doc says "N" gives you "always 32 bit", and we
    get
    # 8 bytes. No wonder why we overflow.
    x86_64$ php -r '$a = unpack("N", pack("N", 0xff )); printf("0x%x\n",
    $a[1]);'
    0xffffffff800000ff # Same. Don't tell me 0xff is too large.

    And now, all the following tests are on a 64 bits _big endian_ host
    (sparc64, running php-5.2.1) :
    sparc64$ uname -mprsv
    OpenBSD 3.8 GENERIC#607 sparc64 SUNW,UltraSPARC-IIi @ 440 MHz, version
    0 FPU
    sparc64$ php -r '$a = unpack("N", pack("N", 0xffff)); printf("0x%x\n",
    $a[1]);'
    0xffff # Ok
    # The same, but prefixing 0000 to the argument :
    sparc64$ php -r '$a = unpack("N", pack("N", 0x0000ffff));
    printf("0x%x\n", $a[1]);'
    0xffffffff8000ffff
    # Weird (and with "N", we stayed on the host byte order this time).
    # Shouldn't 0xffff == 0x0000ffff, even on big endian ? Apparently, yes
    :
    sparc64$ php -r 'printf("0x%x\n", 0x0000ffff);'
    0xffff

    # Also, look at this :
    sparc64$ php -r '$a = unpack("N", pack("N", 41445));
    printf("$a[1]\n");'
    41445
    # And now let's just remove the line feed (\n) from the above printf :
    sparc64$ php -r '$a = unpack("N", pack("N", 41445)); printf("$a[1]");'
    -2147442203

    # Same for 2^16 -1 / 65535 / 0xfff :
    sparc64$ php -r '$a = unpack("N", pack("N", 65535));
    printf("$a[1]\n");'
    65535
    sparc64$ php -r '$a = unpack("N", pack("N", 65535)); printf("$a[1]");'
    -2147418113

    # We get the opposite (bogus with \n, correct without) when converting
    # to little endian and back to host :
    sparc64$ php -r '$a = unpack("L", pack("L", 0xffff)); printf( $a[1]);'
    65535
    sparc64$ php -r '$a = unpack("L", pack("L", 0xffff)); printf(
    $a[1]."\n");'
    -2147418113


    This doesn't help :
    SKIP Generic pack()/unpack() tests
    [ext/standard/tests/strings/pack.phpt] reason: 32bit test only





    ------------------------------------------------------------------------


    --
    Edit this bug report at [url]http://bugs.php.net/?id=40749&edit=1[/url]
    windeler at mediafinanz dot de Guest

Posting Permissions

  • You may not post new threads
  • You may post replies
  • You may not post attachments
  • You may not edit your posts

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139