PHP 5.3でバグ#52001をキャッチ:ポインターと初期化されていない変数

最近発見されたtvv 'omバグをきっかけに。



PHPバージョン5.3.0-5.3.2で次のコードを実行すると、結果はすべての期待を超えました。



<?php

f(0, $$var);

$x = 1;

$y = 2;

echo $x;

function f($a, $b) {};









結果は「2」です。 バグを見つけて修正しました: #52001 要するに、初期化されていない変数用の特別なgag変数へのポインターが消去され、PHPのすべてのCV変数が作成されます。







PHPのソースコードを初めて見たとき、PHPスキャナーとパーサーの柔軟性を確認することから検索を開始しました。 コンパイルが正しく機能していることが判明しました。このためには、パーサーのデバッグモードを有効にする必要がありました。 これは、変数に名前を付け、どの構造が属するかを把握するのに役立ちました。 特に、あらゆる種類のzend_do_ *コンパイラー関数の所有権を理解するため。



次に、関数を呼び出すには、名前とアドレスの2つの異なるモードがあることが明らかになりました。 最初の名前は、コンパイルモードでコンパイラに名前がわからない場合に使用されます。 このモードでは、プロトタイプがコンパイラーに認識されないため、引数はわずかに異なる方法で渡されます。



擬似ランダムに変数アドレスを印刷すると、実際には2つの変数(xとy)がPHP内部で同じアドレスを持っていることがわかりましたが、これは明らかにバグでした。 最初は、名前空間で変数が正しく検索されたことに疑問がありましたが、これは、変数を検索するときに名前空間のハッシュ全体を出力するデバッグを含めることで解消されました。



次のことが判明しました。名前による呼び出しでは、転送された変数がリンクになる可能性があるため、転送された変数に特別なマーキングが行われます(プロトタイプが不明であるため)。



$$ var変数とすべての読み取り変数は、特別な初期化されていない変数として作成されます。 関数を呼び出す変数を抽出するためのコードハンドラーは、渡された値がrefとして使用されることを保証しました。 同時に、ポインターへのポインターを使用して、ポインターの値が非常に特殊な初期化されていない変数に書き換えられます。 新しく割り当てられたメモリと等しくなり、参照カウント= 1になります。



その後、初期化された新しい変数はすべてこのメモリを受け取ります。 「誤った」参照カウントは、これらの変数に書き込むときにコピーされず(コピーオンライト)、すべての新しい変数に共通のメモリが使用されるという事実につながります。 これは、代入を1 = 2にすることが可能であったときのFortranの古いバグと同様に、すべてのデータが同じであるという事実につながります。



それだけです それは面白い経験でした、私は本当にそのようなバグが好きです。



このコメントで技術的な部分の詳細を読むことができます。



All Articles