NSStringメソッドの1つでのスタック破損

職場で知っていた奇妙なクラッシュについて書きたいと思います。



韓国語の文字を含むフォルダーに入ると、クラッシュが安定して発生しました。 問題は、一見無害な次の形式のコードにありました。



NSURLComponents* urlComp = [[NSURLComponents new] autorelease]; ... urlComp.path = path; urlComp.user = username; ...
      
      







ユーザーを設定するとクラッシュします-objc_msgSendを誰かに送信するときにセッター内でEXC_BAD_ACCESSを実行します。 すべての変数は順番に並んでおり、壊れることはありません。 この場合、クラッシュはリリース構成で再現されますが、デバッグ構成では再現されません。 リリース構成でのデバッガーの貧弱な作業を誓って、さらに調べてみましょう。



デバッガーはリリースで変数を出力できないことがよくありますが、どのレジスターでどの変数を逆アセンブルされたリストに含めるかを簡単に確認でき、デバッガーはオブジェクトを通常どおり出力できます(たとえば、po $ r0)。 ユーザー名が無効であることがすぐに明らかになります(私の場合、レジスタはr10です)-po $ r10はオブジェクトではなく番号を表示します。 パスの設定後にr10レジスタの値が変更されたことが、やや遅くなります。



さて、メソッド「-[__ NSConcreteURLComponents setPath:]」で何が起こるかを確認しています。 幸いなことに、それは小さく、「-[NSString(NSURLUtilities)stringByAddingPercentEncodingWithAllowedCharacters:]」と呼ばれると、r10レジスタが飛ぶことがわかります。 送信されたパスがエスケープされたとき。 この機能は既に大きく、顧客は分析のために頭をscりますが、少なくとも入出力を見てみましょう



 0x2ca50aec: push.w {r8, r10, r11} 0x2ca50af0: sub.w sp, sp, #0x1020 0x2ca50af4: sub sp, #0x10 ... 0x2ca50e7a: add.w sp, sp, #0x1020 0x2ca50e7e: add sp, #0x10 0x2ca50e80: pop.w {r8, r10, r11}
      
      





エントリ時に、r10はスタックに保存され、終了時に復元されます。 スタックポインター(sp)が返された順序で返されましたが、スタックの内容は同じではなくなりました-r10値が誤って復元されました。 したがって、パーセントエンコーディングのシステム関数では、スタックが破損しています。



明確にするために、クリーンなテストプロジェクトにサンプルコードを取り入れました。



 NSObject* obj1 = [[NSObject new] autorelease]; NSObject* obj2 = [[NSObject new] autorelease]; NSObject* obj3 = [[NSObject new] autorelease]; NSObject* obj4 = [[NSObject new] autorelease]; NSString* str = @"/Users/zaryanov/Movies/rootfolder/시티 오브 히어로 (City of Heroes)/로니 리 가드너 (1961년부터 2010년까지)는 1985 년에 살인죄로 사형을받은 유타 주에서 총살형 된 미국의 악당이었다. 1984 년에 그는 솔트 레이크 시티에서 강도 동안 바텐더를 살해.m4v"; NSLog(@"%s str %@", __func__, str); NSCharacterSet* charSet = [NSCharacterSet URLPathAllowedCharacterSet]; str = [str stringByAddingPercentEncodingWithAllowedCharacters:charSet]; NSLog(@"%s str %@", __func__, str); NSLog(@"%s obj1 %@ obj2 %@ obj3 %@ obj4 %@", __func__, obj1, obj2, obj3, obj4);
      
      





同時に、ログへの出力中にクラッシュは発生しませんでしたが、予想したとおり、最も問題のある機能(エスケープ)で発生しました。 Abortは__stack_chk_fail関数で機能しました-事実、arm64アーキテクチャはクリーンなテストプロジェクトで有効になっており、そこにスタックチェックがあるようです。 armv7のみを残すと、予想どおり、オブジェクトをログに出力するときにクラッシュが発生します。 いずれにせよ、スタックが破損しています。



「stringByAddingPercentEncodingWithAllowedCharactersクラッシュ」でさらにGoogleを実行すると、いくつかの確認結果が得られます。



https://github.com/Alamofire/Alamofire/issues/206-ただし、ここでは大量のメモリ消費について不満を述べていますが、機能は同じです。

https://gist.github.com/clowwindy/0d800f07a5e95e5c4dd0-これは、スタックを完全に破壊する例であり、いくつかのレジスタではありません。



実際、最初のリンクから論理的な決定を下しました-CFURLCreateStringByAddingPercentEscapesを使用するのはあまり便利ではありませんが、機能します。 同じNSURLComponentsは、すでにエスケープされたパスによって設定できます。



この問題はiOS 8.2でも再現されているため、副皮質にノッチを付ける価値があります。 あなたが私の場合を見ることができるように、クラッシュは別の関数を介して問題の関数の暗黙的な呼び出しのために少し隠され、明らかではないかもしれません。 さて、スタックの損傷により、さまざまな方法で幸運を得ることができます。レジスタのみをフックすると、すぐに検出されない場合があります。



UPD:

openradarでそのようなバグが見つからなかったので、投稿することにしました。 同時に、サンプルをコピーし(ブラウザーからではなく、同じ結果になる可能性があります)、...クラッシュを受け取りませんでした。 これらの象形文字はさまざまな方法でUTF-8でエンコードできることが判明しましたが、ある場合には落ち、別の場合には落ちます。 したがって、文字twoは2つの要素の複合体としてエンコードでき(6バイト-E1 84 89 E1 85 B5になります)、すでにアセンブル済みとしてエンコードできます(3バイトになります-EC 8B 9C)。 2番目のケースでは、そのような線は落ちませんでした。



バグを投稿する-http://www.openradar.me/20404230、バグが再現されるgithubの例へのリンクもあります。



All Articles