VibeGuard Compiler
A research project on security properties that survive in source but not in the compiled binary, tracked at LLVM pass granularity.
The question
A security property you wrote can disappear when the compiler optimises your code. A memset that scrubs a key can be removed as a dead store, because nothing reads the buffer afterwards and the standard lets the compiler say so. Two different permission checks can be folded into the same bytes, because they compute the same thing from the compiler's point of view and differ only in what a human meant by them. The source still says what you meant. The binary no longer does, and nothing in the usual toolchain tells you which of your properties survived.
The method
The work tracks where that happens, one LLVM pass at a time, using the compiler's own public flags rather than a modified compiler: -print-after-all to see the module as each pass leaves it, -opt-bisect-limit to stop the run at the pass that changed the answer, and __attribute__((annotate)) to mark the property being followed so it can be located in the output after inlining has rearranged everything around it.
Working with stock flags is a constraint that pays: a result that needs a patched compiler is a result about that patched compiler, and the question here is what happens to ordinary code under an ordinary toolchain.
What has been measured
One measurement is solid enough to state with its conditions. Compiled at -O2 for x86-64, three functions that mean entirely different things to a reader — an authorization check, a feature-flag test, and a control that was written to be meaningless — came out as the same fourteen bytes of machine code. Same bytes, three names, and no diagnostic anywhere saying that the distinction the programmer wrote had stopped existing.
Secret-scrubbing writes disappear under optimisation too. How often is being re-measured against the zeroing instructions themselves rather than the name of the function that emitted them, so no rate is quoted here yet.
Where the code is
The toolchain's source is in the repository, under compiler/ (https://github.com/YUTAKONDO1205/VibeGuard/tree/main/compiler), together with a README explaining how to build it against a local LLVM. Nothing there is packaged, and none of the four VibeGuard channels carry any of it: the extensions, the CLI and the GitHub Action are built from the TypeScript workspaces and behave identically on a machine where this directory was never built. That boundary is checked by the repository's own packaging invariants, not maintained by hand.
That directory is licensed Apache-2.0 with the LLVM exception rather than the repository's MIT, because it builds against the Clang and LLVM libraries.
Status
A paper about this work is in preparation. It will be linked here once it is published.
概要(日本語)
ソースコードが表明したセキュリティ上の性質は、コンパイラの最適化によって消えることがある。鍵をゼロ埋めするmemset は、その後に誰も読まないという理由で不要な書き込みとして削除されうる。意味の異なる関数が、 計算内容が同じであるという理由で同一のコードに畳み込まれることもある。ソースは書いたとおりの ことを言い続けるが、生成されたバイナリはもうそう言っていない。
この研究は、それがどのパスで起きるかを LLVM のパス単位で追跡する。用いるのは Clang/LLVM が公開している フラグそのもので、-print-after-all で各パス通過後のモジュールを観測し、-opt-bisect-limit で結果が変わったパスまでで停止させ、__attribute__((annotate)) で追跡対象の性質に印を付ける。コンパイラ本体には手を入れない。 改造したコンパイラで得た結果は、その改造したコンパイラについての結果にしかならないからである。
条件を付けた上で言える実測が1つある。x86-64 向けに -O2 でコンパイルしたとき、読み手にとって まったく意味の異なる3つの関数 ── 認可チェック、フィーチャーフラグの判定、そして意味を持たないように 書かれた対照 ── が、同一の14バイトの機械語になった。バイト列は同じ、名前は3つ、そして プログラマが書き分けた区別が消えたことを知らせる診断は、どこにも出ない。
秘密を消去する書き込みも最適化で消える。その頻度については、消去命令そのものを数える方法で測り直している 最中なので、ここでは数値を出さない。
ツールチェーンのソースはリポジトリの compiler/ にあり、ローカルの LLVM に対してビルドする 手順を README に記している。ただし、パッケージ化された成果物は無く、VibeGuard の4つの配信チャネルは いずれもこれを含まない。拡張・CLI・GitHub Action は TypeScript のワークスペースから作られており、この ディレクトリを一度もビルドしていない環境でも同一に動作する。この境界はリポジトリ側の検査で機械的に 保たれている。
この研究についての論文を執筆中である。公表され次第、ここからリンクする。