<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>ASAN</title><link>https://blog.ast.moe/tags/asan/</link><description>Recent posts from 五里霧中</description><generator>Hugo</generator><item><title>BoaがASANのfake stackでスタック使用量を誤判定した</title><link>https://blog.ast.moe/blog/2026-09-10-3/</link><pubDate>Thu, 10 Sep 2026 20:14:12 +0900</pubDate><guid>https://blog.ast.moe/blog/2026-09-10-3/</guid><description>&lt;p&gt;&lt;a href="https://blog.ast.moe/blog/2026-09-10-2/"&gt;前回の記事&lt;/a&gt;では、Boaのパーサーが2 MiBのスタックを使い切る問題を修正しました。&lt;/p&gt;
&lt;p&gt;再帰中のフレームを小さくし、深すぎる入力にはエラーを返すようにしました。通常のビルドでは、元のTest262も2 MiBで通るようになっています。&lt;/p&gt;
&lt;p&gt;その状態でAddressSanitizer（ASAN）を使った診断を進めると、今度は浅い入力まで拒否するようになりました。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;parser recursion limit exceeded
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;スタックを使い切らないように追加した保護が、使い切っていないところで止めていました。&lt;a href="https://github.com/ieee0824/omoikane/issues/640"&gt;Issue #640&lt;/a&gt;の話です。&lt;/p&gt;
&lt;h2 id="同じ実行ファイルなのに設定を変えると通る"&gt;同じ実行ファイルなのに設定を変えると通る&lt;/h2&gt;
&lt;p&gt;最初に確認したのは、Test262の&lt;code&gt;test/built-ins/Function/prototype/constructor&lt;/code&gt;です。&lt;/p&gt;
&lt;p&gt;x86_64 Linux、Rust 1.98.1、ASANを有効にしたreleaseビルドで、workerのスタックは8 MiBありました。&lt;/p&gt;
&lt;p&gt;同じ実行ファイルとテストを使い、ASANの設定だけを切り替えたところ、結果が変わりました。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;detect_stack_use_after_return&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;結果&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0/1。&lt;code&gt;assert.js&lt;/code&gt;の読み込み時に再帰制限へ到達&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1/1。normalとstrictの両方で成功&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;比較ログは&lt;a href="https://github.com/ieee0824/boa/actions/runs/34409886173"&gt;当時のCI run&lt;/a&gt;のartifactにある&lt;code&gt;calibration-1.log&lt;/code&gt;と&lt;code&gt;calibration-0.log&lt;/code&gt;に残しています。&lt;/p&gt;
&lt;p&gt;テスト本体の複雑な処理どころか、テスト用の&lt;code&gt;assert.js&lt;/code&gt;を読み込む段階で止まっています。前回の深い入れ子とは少し様子が違いました。&lt;/p&gt;
&lt;h2 id="ローカル変数のアドレスで測っていた"&gt;ローカル変数のアドレスで測っていた&lt;/h2&gt;
&lt;p&gt;前回追加したスタック使用量の検査では、解析の入口で小さなローカル変数を作り、そのアドレスを記録していました。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; marker &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0_&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;u8&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cursor.enter_parser(std::ptr::from_ref(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;marker) &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;usize&lt;/span&gt;, interner)&lt;span style="color:#f92672"&gt;?&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;一番外側の解析で記録したアドレスと、現在の解析で得たアドレスの差を、スタック使用量として扱う方式です。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;外側の解析にあるmarkerのアドレス
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ↓ アドレスの差
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;再帰先の解析にあるmarkerのアドレス
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;ここには、両方の&lt;code&gt;marker&lt;/code&gt;が同じnative stack上に置かれているという前提があります。&lt;/p&gt;
&lt;p&gt;ASANのfake stackを有効にすると、この前提が崩れました。&lt;/p&gt;
&lt;h2 id="fake-stackでは変数が別の場所に置かれる"&gt;fake stackでは変数が別の場所に置かれる&lt;/h2&gt;
&lt;p&gt;ASANには、関数から戻ったあとで、その関数のローカル変数を参照してしまう不具合を検出する機能があります。stack-use-after-returnの検査です。&lt;/p&gt;
&lt;p&gt;通常のスタック領域は、関数から戻ると次の呼び出しで再利用されます。そこでASANは検査対象のローカル変数をfake stackという別の領域へ配置し、関数が終わったあとにその領域へのアクセスを検出できるようにします。&lt;a href="https://github.com/google/sanitizers/wiki/AddressSanitizerUseAfterReturn"&gt;ASANの設計文書&lt;/a&gt;に仕組みが説明されています。&lt;/p&gt;
&lt;p&gt;この配置では、ローカル変数のアドレス同士が遠く離れていても、その分だけnative stackを消費したとは限りません。&lt;/p&gt;
&lt;p&gt;今回の実装は、その距離をそのまま使用量として扱っていました。浅い解析でも1.5 MiBを超えたと判定され、保護処理がエラーを返していたわけです。&lt;/p&gt;
&lt;p&gt;ASANが不正アクセスを報告して止めたのではなく、ASANによって変わった変数配置をパーサー側が読み違えていました。&lt;/p&gt;
&lt;h2 id="スタックポインタを直接読む"&gt;スタックポインタを直接読む&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/ieee0824/boa/pull/89"&gt;Boa PR #89&lt;/a&gt;では、x86_64とARM64について、CPUのスタックポインタを直接読む方式に変更しました。&lt;/p&gt;
&lt;p&gt;x86_64では&lt;code&gt;rsp&lt;/code&gt;を読みます。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; address: &lt;span style="color:#66d9ef"&gt;usize&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;unsafe&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; core::arch::&lt;span style="color:#a6e22e"&gt;asm!&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;mov {}, rsp&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; out(reg) address,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; options(nomem, nostack, preserves_flags)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;ARM64では&lt;code&gt;sp&lt;/code&gt;を読みます。&lt;/p&gt;</description></item></channel></rss>