PerlのSVPV脅威


あなたが私またはポストを読んだならば、あなたはPerlで文字符号化の複雑さについて知っています.私が最初のポストを書いたあと、私は少しのエピファニーを持ちました.
ある日気づいたURI::XSEscape その出力をマンリングしていたépée 出かける%C3%83%C2%A9p%C3%83%C2%A9e . 私はこれを余分なUTF - 8エンコードとして認識しました.épée , UTF - 8エンコーディングで、現在10バイト、それからURIエンコードをエンコードしました.
引き抜いたDevel::Peek そして、URI符号化ステップの前の何かが私のストリングの内部記憶装置を「アップグレード」したのを見ました:Perlスカラがまだ6つのキャラクタから成ったとしても、Perl自体は私のストリングを10バイトとして保存しました.Perlコードがどのように文字列を格納するかを気にする必要がないので、通常これは重要ではありません.
...それが気にかけるまで、それはそうです.

SVPVとは
PerlのC APIはCからPerlで動作するマクロと関数のセットです.PerlのスカラーをC符号付き整数に変換するにはSvIV , SvIV_nomg , SvIVX , or SvIVx . ( IV ここでは、“整数値”を示します.同様のセットのマクロは符号なし整数に対して存在します(UV です.
PerlのスカラをC文字列に変換するのも同様です.利用できるツールはたくさんありますが、3つの基本的なものは以下の通りです.
  • SvPVbyte : Perlの文字列のコードポイントを取り、それらのコードポイントと一致するCバッファを返します.したがって、255を超えるコードポイントは動作しません.例外がスローされます.
  • SvPVutf8 : ライクSvPVbyte しかし、Perl文字列のコードポイントにUTF - 8エンコードされたバイトを与えます.これはPerlが保存することができるコードポイントに対して動作しますが、コードポイント128 - 255に対してはSvPVbyte . (cf. perldoc perlunicode )
  • SvPV : Perlのストリングの内部バッファを与えます、別名「PV」(「ポインタ値」).それはバイトかもしれません.これはCのアナログのようなPerlのuse bytes .
  • SvPV , もちろん、uri ::xsescapeが使用していたものです.
    For SvPV 意味があるために、それはタンデムで使われなければなりませんSvUTF8 , pvがどの形式であるかを示すマクロです.それでSvUTF8 では、SvPV ’s出力はUTF - 8です.otherwise SvPV ’s出力はバイトです.しかし、uri ::xsescapeはチェックされませんでしたSvUTF8 ; URI符号化だけでしたSvPV 直接.
    との大きな問題SvPV は、PerlやUTF - 8であるC文字列を持つことが賢明なPerl以外のコンテキストの数です.それにもかかわらず、Perlの外の文脈と相互作用するこのマクロ(そして、その変形)の使用は、CPANの上にあります.
    URI::XSEscape, like its pure-Perl counterpart , “バイト指向”と“文字指向”のPerlコード(CF)の両方に適したインターフェイスを示します.バイト指向のインターフェースが私が使用していたので、スイッチングURISvPV to SvPVbyte この問題に対する単純な修正はありました.
    本質的には、URI ::xsescapeのようなCコードはPerlの文字列記憶について気にかけることなく、PerlのPerlコードが同じ方法でPerl文字列に近づくべきです.したがって、ほとんどのCコードは避けるべきですSvPV ほとんどのPerlがそうしない理由use bytes .

    陰謀が濃くなる.
    若干の人気のXSモジュールを通した速い走査は、この問題のより多くの出現を示しました:
  • DBD::SQLite
  • Net::Curl

  • DNS::Unbound (メー・カルパ!)
  • DNS::LDNS
  • YAML::Syck
  • HTTP::Parser::XS

  • Socket (コアモジュール!)
  • デフォルトではUTF - 8にエンコードされますが、デフォルトの設定には同じバグがあります.
  • CDB_File
  • LMDB_File
  • さらに多くの可能性がありますそれらは私が見つけたちょうどものです.

    どうやってこれがやったの?
    たぶんそうだろう.
  • SvPV はPerlのスカラーをC文字列に変換するための上記のメソッドの最短です.このように、それはタイプをより簡単で、より「脅迫的」に見えます.
  • 歴史的に、Perlのドキュメントは支持されますSvPV スカラー文字列変換の例では他の2つはめったに議論されなかった.I fixed this recently , しかし、誰もが地元の前に何年もされますperldoc その変化を反映します.
  • PerlのデフォルトXS typemap 用途SvPV なしでSvUTF8 ) スカラーを文字列に変換するにはしたがって、以下のXSUBprintstr($mystr) :
  • void
    printstr (const char *str)
      CODE:
        fprintf(stdout, str);
    
    ... Perlの呼び出し元が気にすると思われていないPerlの内部を印刷します.理想的な言語のデフォルトは、このようなことをする「安全な」方法であるが、この特定のものは無意味である.

    この問題はあなたのコードに影響しますか?
    この問題をテストする簡単な方法はutf8::upgrade 128 - 255の範囲のコードポイントをテストしていることを確認するために、あなたの文字列をテストコードに与える前に.テストはプログラムの動作が同じであることを検証しますutf8::upgrade 非アップグレードされた文字列と同じ文字列.
    通常、プロダクションで手動で文字列をアップグレードすることはありません(PerlコードはPerlの内部について考えていますので、そうしないでください).
    例えば、以下のようにしてURI ::xsescape問題を見つけました.
    my $foo = "épée";
    utf8::upgrade($foo);
    print URI::XSEscape::uri_escape($foo);
    

    だけでなく、任意の古いバグ.
    これの最悪の部分はcdbrestファイルのようなモジュールが置換できないことですSvPV それに依存するかもしれない既存のアプリケーションを壊すことなくuse bytes -行動.それで、新しい、修正されたインターフェースを構築するのを除いて多くのことをするだけでありません.Perlの“Gurus”では、単純であるかもしれませんが、既存のコードを変更する他の皆のために、後方互換性を優先する言語として、Perlの評判にとって、高価で、痛い、そしてさらに有害かもしれません.

    しかし、それはすべてではない.
    XSコードは、このバグが表示される唯一の場所ではありませんPerl自体も持っています!“で”すべてを読む.

    どうやってこれを修正できますか?
    私はほとんどのコードを使用すると思うSvPV Perl文字列をC文字列に変換するには、Cの文字列のバイトに対応するPerlコードポイントを対象としますしたがって、そのようなコードは実際に使用すべきですSvPVbyte またはその変形のいずれか.( UTF - 8 - aware Cコードはもちろん、SvPVutf8 .) その目的のために、我々はさらなる使用を阻止しなければならないSvPV . 私はPerlコミュニティに、いくつかの変更を提案します.いくつかは何かを壊さないでください.

    これを固定:簡単な部品!
    1 )リネームSvPV そして、友人.私たちはそれらを削除することはできませんが、私たちは長く、“scarier探し”のエイリアスを作成することができますこれらの名前をドキュメントに使用します.提案するSvPVinternal , SvPVinternal_const , など
    2 ) makexsubpp SVEVまたは変種をtypemapで見ると警告します.
    3 ) Perl独自のバギー動作を修正するすべての新しいコードでの使用.
    バグレポートを提出!あなたが使用するXSモジュールを監査してください、そして、あなたがアップグレードされた、そして、階下のストリングの間で異なるふるまいを見つけるならば、メンテナは彼らにパッチを送ることによって理想的に知っていてください!

    これを固定する:難しい部分...
    いくつかの卵を壊すことなくオムレツを作ることはできません.それにもかかわらず.
    5 ) makechar * and const char * Perlのデフォルトtypemap使用でSvPVbyte . (実際)SvPVbyte_nolen , でもねえ.)XSモジュールの大多数については、これはおそらくバグフィックスになるでしょうuse bytes -現状は壊れている.ありがたいことに、しかし、最も広く使われているXSモジュール(例えば).MIME::Base64 , JSON::XS ) これは問題である可能性があります脆弱性、およびBの任意の破損を修正するのは簡単です:モジュールの著者を採用する必要がありますSvPVutf8 それが彼らが望むものであるならば、両方の支持が望ましいならば、別々に機能をつくってください.
    6 ) makeSys::Binmode ’s自身のふるまいの振る舞い.これは、WindowsファイルシステムのPerlのlacklustreサポートのはるかに大きな問題を回避するので、より満足していますそれでも、sys ::binmodeタイプふるまいはPerlの状態quoより悪くありません、そして、それはPerlのストリング抽象化で重大なリークを修正します.

    これを固定する:ムーンショット.
    7 ) Perlはバイト列をテキスト文字列から区別する必要があります.これは、言語のユーザーを悩ませる“シンバンパー”の多額を修正します.これは解決するのにかなり難しい問題です、しかし、私はそれが克服できないと思いません.

    その間.
    上記のような不在の修正は、ちょうどこの問題を避けなければなりません.エンコードされた文字列をオペレーティングシステムに送り、出力前にそれらを格下げすると、常に一貫した振る舞いをするでしょう.このようにPerlはどんな文字列もUTF - 8として保存しませんSvPV and SvPVbyte 同じ結果を与える.
    重要:もしあなたの文字列をデコードしないなら、定義によって既にエンコードされているので、この場合は手動でエンコードしないでください.そうすれば出力を混乱させるでしょう.