Tom: Verhalten von PHP-Funktionen abhängig vom Compiler?

Hello,

wir haben da noch ein Problem, dass ich nicht alleine gelöst bekomme.

In einer Crypt Funktion, die mit shift_left arbeitet, treten angeblich unterschiedliche Ergebnisse auf, je nachdem, ob PHP mit gcc 3.3 oder mit einer neueren Version compiliert wurde.

Kann das jemand nachvollziehen?

"I have the similar problem, the reason was in PHP that was builded with gcc 3.4. When arithmetic overflow occurs PHP sets number value in 2^32 instead cutting highest bit...we build php with gcc 3.3 and all works fine"

#----------------------------------------------------------------------------------------
//unsigned shift right
function zeroFill($a, $b)
{
    $z = hexdec(80000000);
        if ($z & $a)
        {
            $a = ($a>>1);
            $a &= (~$z);
            $a |= 0x40000000;
            $a = ($a>>($b-1));
        }
        else
        {
            $a = ($a>>$b);
        }
        return $a;
}

#----------------------------------------------------------------------------------------

function mix($a,$b,$c)
{
  $a -= $b; $a -= $c; $a ^= (zeroFill($c,13));
  $b -= $c; $b -= $a; $b ^= ($a<<8);
  $c -= $a; $c -= $b; $c ^= (zeroFill($b,13));
  $a -= $b; $a -= $c; $a ^= (zeroFill($c,12));
  $b -= $c; $b -= $a; $b ^= ($a<<16);
  $c -= $a; $c -= $b; $c ^= (zeroFill($b,5));
  $a -= $b; $a -= $c; $a ^= (zeroFill($c,3));
  $b -= $c; $b -= $a; $b ^= ($a<<10);
  $c -= $a; $c -= $b; $c ^= (zeroFill($b,15));

return array($a,$b,$c);
}

#----------------------------------------------------------------------------------------

function crypt_url($url, $length=null, $init=CRYPT_MAGIC)
{
    if(is_null($length))
    {
        $length = sizeof($url);
    }

$a = $b = 0x9E3779B9;
    $c = $init;
    $k = 0;
    $len = $length;

while($len >= 12)
    {
        $a += ($url[$k+0] +($url[$k+1]<<8) +($url[$k+2]<<16) +($url[$k+3]<<24));
        $b += ($url[$k+4] +($url[$k+5]<<8) +($url[$k+6]<<16) +($url[$k+7]<<24));
        $c += ($url[$k+8] +($url[$k+9]<<8) +($url[$k+10]<<16)+($url[$k+11]<<24));
        $mix = mix($a,$b,$c);
        $a = $mix[0]; $b = $mix[1]; $c = $mix[2];
        $k += 12;
        $len -= 12;
    }

$c += $length;

switch($len)              /* all the case statements fall through */
    {
        case 11: $c+=($url[$k+10]<<24);
        case 10: $c+=($url[$k+9]<<16);
        case 9 : $c+=($url[$k+8]<<8);
          /* the first byte of c is reserved for the length */
        case 8 : $b+=($url[$k+7]<<24);
        case 7 : $b+=($url[$k+6]<<16);
        case 6 : $b+=($url[$k+5]<<8);
        case 5 : $b+=($url[$k+4]);
        case 4 : $a+=($url[$k+3]<<24);
        case 3 : $a+=($url[$k+2]<<16);
        case 2 : $a+=($url[$k+1]<<8);
        case 1 : $a+=($url[$k+0]);
         /* case 0: nothing left to add */
    }
    $mix = mix($a,$b,$c);
    /*-------------------------------------------- report the result */
    return $mix[2];
}

Wenn mich nicht alles täuscht, ist das ein MD-5 crypt-Algorythmus

Server 1 PHP 4.3.3 läuft nicht
Server 2 PHP 4.4.0 läuft wunschgemäß

Wie kann man feststellen, mit welcher GCC-Vewrison PHP compiliert wurde?

Harzliche Grüße vom Berg
http://bergpost.annerschbarrich.de

Tom

--
Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
Nur selber lernen macht schlau
Ein Jammer ist auch, dass die Dummen so selbstsicher und die Klugen voller Zweifel sind. Das sollte uns häufiger zweifeln lassen :-)

  1. Hallo Tom,

    In einer Crypt Funktion, die mit shift_left arbeitet, treten angeblich unterschiedliche Ergebnisse auf, je nachdem, ob PHP mit gcc 3.3 oder mit einer neueren Version compiliert wurde.

    Kann das jemand nachvollziehen?

    "I have the similar problem, the reason was in PHP that was builded with gcc 3.4. When arithmetic overflow occurs PHP sets number value in 2^32 instead cutting highest bit...we build php with gcc 3.3 and all works fine"

    Naja, der Text sagt es doch schon: Wenn ein Überlauf auftritt, dann reagiert PHP anders. Die Frage ist halt: Wann tritt ein Überlauf auf?

    [Viel Code]
    Wenn mich nicht alles täuscht, ist das ein MD-5 crypt-Algorythmus

    Naja, das sieht nicht nach MD5 aus, aber es ist definitiv eine Form von Hash-Algorithmus.

    Übrigens: Du hast vergessen, die CRYPT_MAGIC-Konstante zu posten, so ist der Code relativ nutzlos.

    Zudem: Ohne Dir zu nahe tretenzu wollen, die Funktion ist doch irgendwie Käse, oder? Wenn $url ein String ist (was der Name der Funktion nahe legt), dann wird $url[2] bei 'http://www.heise.de/' (als Beispiel) zum Zeichen 't' ausgewertet, was dann - wenn's in einen Integer verwandelt wird - 0 ergibt. Und sizeof() ist bei Strings auch nicht extrem nützlich. Und einer Funktion, die crypt_url() heißt einen Hash von Bytewerten zu übergeben ist doch auch seltsam...?

    Server 1 PHP 4.3.3 läuft nicht
    Server 2 PHP 4.4.0 läuft wunschgemäß

    Urgs. Das ist nicht Dein Ernst? PHP 4.4.0? 4.3.3? Sicherheit, anyone? Aktuellste PHP-Version der 4er-Reihe ist 4.4.7. Alle Versionen davor ist Selbstmord und selbst 4.4.7 ist sicherheitstechnisch bedenklich (zumindest ohne zusätzliche Patches, weil die PHP-Leute z.B. Reference Counting Overflows partout nicht fixen wollen, die meisten Distros bieten selbstgepatchte Versionen an, vgl. http://news.php.net/php.internals/29582).

    Und naja, ansonsten: Was heißt "läuft nicht" vs. "läuft wunschgemäß"?

    Wofür willst Du den Kram überhaupt einsetzen? Warum sind Dir die vorgefertigen Hashfunktionen, die PHP bietet (md5, sha1, crc32, ...) nicht ausreichend?

    Wie kann man feststellen, mit welcher GCC-Vewrison PHP compiliert wurde?

    Noch gar nicht. Aktuell wird auf der Mailingliste diskutiert, dass diese Information in Zukunft gespeichert wird: http://news.php.net/php.internals/33168 Kommt also frühestens 5.2.6.

    Viele Grüße,
    Christian

    1. Hello,

      Naja, der Text sagt es doch schon: Wenn ein Überlauf auftritt, dann reagiert PHP anders. Die Frage ist halt: Wann tritt ein Überlauf auf?

      [Viel Code]
      Wenn mich nicht alles täuscht, ist das ein MD-5 crypt-Algorythmus

      Naja, das sieht nicht nach MD5 aus, aber es ist definitiv eine Form von Hash-Algorithmus.

      Übrigens: Du hast vergessen, die CRYPT_MAGIC-Konstante zu posten, so ist der Code relativ nutzlos.

      Das war eigentlich Absicht. Man kann ja irgendeine nehmen, um den angeblichen Verhaltensunterschied nachzuvollziehen, sofern man eine PHP-Versionen hat, die wahlweise mit unterschiedlichen Compilerversionen compiliert wurde. Oder sehe ich das falsch?

      Zudem: Ohne Dir zu nahe tretenzu wollen, die Funktion ist doch irgendwie Käse, oder?

      Tust Du nicht. Die Funktion ist nicht von mir. Aber sowas ging mir vorhin auch durch den Kopf...
      Leider kann ich im Moment nix ausprobieren.

      Wenn $url ein String ist (was der Name der Funktion nahe legt), dann wird $url[2] bei 'http://www.heise.de/' (als Beispiel) zum Zeichen 't' ausgewertet, was dann - wenn's in einen Integer verwandelt wird - 0 ergibt. Und sizeof() ist bei Strings auch nicht extrem nützlich. Und einer Funktion, die crypt_url() heißt einen Hash von Bytewerten zu übergeben ist doch auch seltsam...?

      Da hat wahrscheinlich einer Pascal und PHP gemischt. *tztz*
      Aber da war sizeof(), soweit ich mich erinnere, für Dateigrößen.
      Laut PHP-Manual müsste immer 1 rauskommen bei Strings.

      Server 1 PHP 4.3.3 läuft nicht
      Server 2 PHP 4.4.0 läuft wunschgemäß

      Urgs. Das ist nicht Dein Ernst? PHP 4.4.0? 4.3.3? Sicherheit, anyone? Aktuellste PHP-Version der 4er-Reihe ist 4.4.7. Alle Versionen davor ist Selbstmord und selbst 4.4.7 ist sicherheitstechnisch bedenklich (zumindest ohne zusätzliche Patches, weil die PHP-Leute z.B. Reference Counting Overflows partout nicht fixen wollen, die meisten Distros bieten selbstgepatchte Versionen an, vgl. http://news.php.net/php.internals/29582).

      Hab ich nicht wirklich Einfluss drauf...

      Und naja, ansonsten: Was heißt "läuft nicht" vs. "läuft wunschgemäß"?

      Das bedeutet, dass der Client die Funktion auf seine übermittelten Daten anwendet und der Server das auch tut und dadurch feststellen können soll, ob der Client berechtigt ist. Darum kann der Client auch nicht einfach die Funktion austauschen. Dann würde es nicht mehr zusammenpassen.

      Aber das mit 0 + 0 + 0 + 0 ist schon lustig.

      Wofür willst Du den Kram überhaupt einsetzen? Warum sind Dir die vorgefertigen Hashfunktionen, die PHP bietet (md5, sha1, crc32, ...) nicht ausreichend?

      Diese Funktionen sind Bestandteil eines Partnervertrages und kommen von diesem Partner (Server).
      Ich habe sie vom Client mit der Bitte, nach dem Fehler zu suchen, bekommen.

      Diese Frage stammte von mir:

      Wie kann man feststellen, mit welcher GCC-Vewrison PHP compiliert wurde?

      Noch gar nicht. Aktuell wird auf der Mailingliste diskutiert, dass diese Information in Zukunft gespeichert wird: http://news.php.net/php.internals/33168 Kommt also frühestens 5.2.6.

      Harzliche Grüße vom Berg
      http://bergpost.annerschbarrich.de

      Tom

      --
      Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
      Nur selber lernen macht schlau
      Ein Jammer ist auch, dass die Dummen so selbstsicher und die Klugen voller Zweifel sind. Das sollte uns häufiger zweifeln lassen :-)

      1. Hallo Tom,

        Naja, als Tipp kann ich Dir folgendes geben:

        1. Du brauchst eine komplette, programmiersprachenunabhängige Spezifikation des Algorithmus (als Beschreibung in Worten, die eindeutig festlegt, was der Algorithmus tun soll). Frag halt den, der den Algorithmus erfunden hat, danach.

        2. Du brauchst mehrere korrekte Testcases, sprich: Eingaben und zugehörige Ausgaben. Die kannst Du Dir mit der Spezifikation auch generieren, indem Du die Schritte des Algorithmus manuell durchgehst (was zwar aufwändig ist, dafür hast Du eine unabhängige Kontrolle).

        3. Du überprüfst beide Implementierungen (Client, Server) des Algorithmus mit den Testdaten und schaust, was bei rauskommt.

        Was mir allerdings auffällt, woran es liegen könnte: Die Funktionen gehen alle davon aus, dass alle Integer-Werte nur 32 bit haben. Betrachte folgenden Beispielcode:

        printf ("%016x\n", 0x40000000 << 4);

        Auf einem 32bit-System ergibt das:

        0000000000000000 (also Null)

        Auf einem 64bit-System dagegen:

        0000000400000000

        Da die bisherige Implementiernug durch das '+' und '-' (also Addition und Subtraktion) aber auch auf 32bit bereits nicht vollkommen korrekt 32bit-artig reagiert (nimmt float-Umwandlungen von PHP bei zu großen Zahlen in Kauf!), wird es EXTREM schwierig das Verhalten der Funktion auf 32bit auf 64 nachzubauen... Wenn das denn tatsächlich die Ursache ist.

        Ganz ehrlich: Die Funktion ist Schrott (vmtl. ein 1:1-Port von einer C-Funktion, bei der der Programmierer allerdings keinerlei Eigenheiten von PHP beachtet hat) und Du solltest die Leute dazu überreden, etwas anderes zu verwenden. Denn ich bezweifle, dass Du es ohne zu großen Aufwand hinbekommst, da was richtig hinzukriegen.

        Viele Grüße,
        Christian

        1. Hello,

          [...] Danke für die shl-Erläuterung

          Ganz ehrlich: Die Funktion ist Schrott (vmtl. ein 1:1-Port von einer C-Funktion, bei der der Programmierer allerdings keinerlei Eigenheiten von PHP beachtet hat) und Du solltest die Leute dazu überreden, etwas anderes zu verwenden. Denn ich bezweifle, dass Du es ohne zu großen Aufwand hinbekommst, da was richtig hinzukriegen.

          $url ist kein String, sondern ein Array of ord(char) des Strings.
          In sofern hat die Funktion schon einen Sinn.

          Das wir in der Aufrufenden Funktion umgesandelt. Die hatte ich aber nicht mutgeschickt, also konntest Du das nicht wissen. Und ich hatte sie noch nicht auseinandergenommen.

          function strord($string)
          {
            for($i=0;$i<strlen($string);$i++)
            {
              $result[$i] = ord($string{$i});
            }
            return $result;
          }

          Dieses $result wird dann für $url eingesetzt.

          Diese Scripte werden von vielen Leuten eingesetzt. Wenn das bei allen nicht mehr laufen würde, wäre der Teufel los.

          Nun zur eigentlichen Vermutung:
          Der Serverbetreiber hat die Funktion von prozedural in OOP umgeschrieben. Ich habe inzwischen die neue zu Gesicht bekommen.

          Variante A: Da muss irgendwo ein klitzekleiner Unterschied drin sein.

          Variante B: auf dem Server (der Client spielt) mit PHP 4.3.3 läuft auch
                      die neue Funktion nicht so, dass das Ergebnis zur Serverseite passt.
                      Auf dem mit 4.4. liefert sie aber das gewünschte Ergebnis.

          Auf einem mit php5.2 würde sie wahrscheinlich auch wunschgemäß arbeiten

          Soweit ich mich erinnere, sind bezüglich OOP auch schon in der 4er Serie häufiger Änderungen durchgeführt worden. Zur 5er dann ja sowieso. Da die Class aber nur als "Behälter" benutzt wird, sehe ich da keine Probleme. Es könnte aber zwischen 4.3.3. und 4.4 einen Interpretationsunterschied bei OOP geben.

          Es muss also nicht unbedingt an einem Überlauf liegen.

          Da die Maschine ohnehin neu gamacht werden muss, ist dann auch gleich PHP5.2 fällig.
          Ich habe nur überhaupt keine Ahnung, wie das mit Confixx zusammenpasst. Da werde ich ja nicht einfach was ändern dürfen an den Modulen und der ini.

          Harzliche Grüße vom Berg
          http://bergpost.annerschbarrich.de

          Tom

          --
          Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
          Nur selber lernen macht schlau
          Ein Jammer ist auch, dass die Dummen so selbstsicher und die Klugen voller Zweifel sind. Das sollte uns häufiger zweifeln lassen :-)

          1. echo $begrüßung;

            Soweit ich mich erinnere, sind bezüglich OOP auch schon in der 4er Serie häufiger Änderungen durchgeführt worden. Zur 5er dann ja sowieso. Da die Class aber nur als "Behälter" benutzt wird, sehe ich da keine Probleme. Es könnte aber zwischen 4.3.3. und 4.4 einen Interpretationsunterschied bei OOP geben.

            Der Grund für die Änderung von 4.3 auf die 4.4er Serie ist im Referenzhandling begründet. Beispielsweise wenn eine Funktion eine Referenz zurückgeben soll, aber keine Variable (zu der man eine Referenz erzeugen kann) sondern einen Ausdruck (zu dem man keine Referenz erzeugen kann) zurückgab, gab es früher stillschweigend Probleme, nun eine Notice-Meldung.

            function &foo() {
              $bar = 42;
              return ($bar); // die Klammern machen aus der Variable einen Ausdruck.
            }

            Das war die wesentlichste Änderung bei diesem Versionssprung. PHP4 war zu dem Zeitpunkt aus der Sicht der Entwicklung schon relativ gegessen, so dass im Wesentlichen nur noch Bugfixe einflossen.

            Ansonsten gibt es das Changelog, in dem alle auch kleineren Veränderungen erwähnt werden.
            PHP 4 ChangeLog
            PHP 4.4. Release Announcement

            echo "$verabschiedung $name";

            1. Hello,

              function &foo() {
                $bar = 42;
                return ($bar); // die Klammern machen aus der Variable einen Ausdruck.
              }

              Danke für den Tipp. Danach schau ich mal als erstes.

              Muss doch zu finden sein, warum das plötzlich Probleme gibt. Die Schlumpies beim Server-Betreiber haben aber auch kein Wort davon gesagt, dass sie was geändert haben.

              Harzliche Grüße vom Berg
              http://bergpost.annerschbarrich.de

              Tom

              --
              Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
              Nur selber lernen macht schlau
              Ein Jammer ist auch, dass die Dummen so selbstsicher und die Klugen voller Zweifel sind. Das sollte uns häufiger zweifeln lassen :-)

              1. Hallo,

                Muss doch zu finden sein, warum das plötzlich Probleme gibt.

                Es gibt plötzlich Problem, weil die Funktion einfach Schrott ist. Sie reagiert auf unterschiedlichen Systemen einfach vollkommen unterschiedlich - und ich kann mir sehr gut vorstellen (sagte ich bereits), dass unterschiedliche Compiler- oder PHP-Versionen auch nochmal unterschiedlich reagieren.

                Wenn man Bitoperationen in PHP implementiert, muss man höllisch aufpassen - auch wegen der Tatsache dass es bei PHP auf Grund der schwachen Typisierung nicht möglich ist, bestimmte Datentypen zu forcieren ohne zusätzlichen Aufwand. Die Funktion ist schlichtweg nicht portabel und alle Versuche, auf anderen Systemen das exakte (!) Verhalten der Funktion auf einem bestimmten System nachzustellen MUSS mit einer _kompletten_ (!) Emulation der jeweiligen CPU sowie des Verhaltens von PHP in der spezifischen Version auf dieser CPU einhergehen - was hochgradig nichttrivial ist und in meinen Augen den Aufwand einfach nicht wert. Nehmt eine andere Funktion, das ist der einzige sinnvolle Ratschlag, den man erteilen kann.

                Viele Grüße,
                Christian

                1. echo ($light == true) ? 'Guten Tag,' : 'Guten Abend,';

                  also wenn mich nicht alles täuscht ist das eine Fehlerhafte Implementierung des Hash-Algorithmus der notwendig ist um einen Pagerank  abzufragen. Vermutlich ist der eine Server ein 32 Bit Sytem und der andere Server ein 64 Bit System was diese Probleme erklären würde. Alles weitere dazu wirst du bei g00gle sicher selbst herausfinden können.

                  Grüße

                  Markus

                  --
                  Das Böse ist grau ... color:#666;
                  1. Hallo,

                    Du hast Recht, wenn man mal danach googlet, dann stößt man auf die entsprechende Funktion.

                    Toll, Tom, warum sagst Du nicht einfach, wozu Du die Methode brauchst? Das hätte mir eine MENGE Arbeit erspart, mich in die Funktion reinzudenken, ich hätte dann nämlich einfach geschickt Google nutzen können und dabei folgendens finden können:

                    http://v1.magicbeandip.com/mbd-file/PageRankReport.php

                    Das funktioniert nach Aussage des Autors auch auf 64bit-Systemen, getestet habe ich's nicht.

                    Viele Grüße,
                    Christian

                    1. Hello,

                      Toll, Tom, warum sagst Du nicht einfach, wozu Du die Methode brauchst?

                      Sorry, ich wollte Dich damit nicht ärgern. Ist nicht für mich und da halte ich mich mit Daten immer zurück.

                      Das hätte mir eine MENGE Arbeit erspart, mich in die Funktion reinzudenken, ich hätte dann nämlich einfach geschickt Google nutzen können und dabei folgendens finden können:

                      Das hat aber eine Menge Anregungen gegeben, die für mich wertvoller sind, als ein fertiges Script. Ich danke Dir dafür besonders!

                      http://v1.magicbeandip.com/mbd-file/PageRankReport.php

                      Das funktioniert nach Aussage des Autors auch auf 64bit-Systemen, getestet habe ich's nicht.

                      Das hat mir der Freund inzwischen auch schon von einem anderen Anbieter aus der SEO-Community der besagten Seite angeschleppt. Da hat es zwar sauberer geschrieben, aber hat auch nicht weniger Fehler *g*

                      Ich habe es inzwischen soweit ich konnte gefixt. Es läuft auch auf dem besagten Server und erzeugt den Request, wenn ich diesen an meinen Testserver (*puh* ne uralte Platte von 2001),der über Dyn-DNS angeschlossen ist, richte. Der kennt natürlich den Rank nicht.
                      Aber, wenn ich das Script dann von meinem Server aus benutze und bei Google anfrage, erzeugt es genau den gleichen Request, wie ich ihn erhalten habe und ich erhalte Antwort.

                      Wenn ich es nun auf dem besagten Root-Server bei Strato benutze, und bei Google anfrage, kommt wieder nur die Fehlermeldung. Da ich nicht ersehen kann, wo da etwas anderes erzeugt worden sein könnte,  als wenn ich den Request an mich schicke, muss ich annehmen, dass die Requests des Root-Server bei Strato, die an Google gerichtet sind, nicht sauber durchlaufen.

                      Das gleiche ist übrigens, wenn ich den Request auf dem Root-Server an sich selber (an ein responder-Script) richte. Dann kommt nur noch Müll raus.

                      Wenn ich dasselbe responder-Script aber mittels des Rank-Scriptes von meinem Server aus mit

                      GET /tom/listheader.php  HTTP/1.1\r\n

                      aufrufe, kommt nur Müll dabei heraus.
                      Nehme ich aber HTTP 1.0 bekomme ich eine saubere Antwort.

                      Da muss ich also mit HTTP 1.1 noch was nicht beachtet haben.

                      Aber für die Anfrage bei Google ändert das leider nichts. Die Antwort bleibt weiter bei 403.

                      Harzliche Grüße vom Berg
                      http://bergpost.annerschbarrich.de

                      Tom

                      --
                      Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
                      Nur selber lernen macht schlau
                      Ein Jammer ist auch, dass die Dummen so selbstsicher und die Klugen voller Zweifel sind. Das sollte uns häufiger zweifeln lassen :-)