Canaryの大きさを指定できるようにする
clang 7.0, LLVM 7.0を拡張
とりあえずgcc attributeみたいな感じで指定できるようにしたい
イメージ `char __attribute__((multicanary_size=(256))) hoge[256];`
もしくは、行列もしくは多次元配列の大きさだけ指定して推定させる
`int __attribute__((multicanary_sizefor=(2, 3))) huga[2][3];`
完全にコンパイラにやらせる
そもそも__attribute__ってC系言語限定なのでは, rustにも似たような機能があった気がするが、[- そもそも関数に対して適用するものだし](variable attributesなるものを発見した)、LLVMに変数宣言ごとの属性を伝えるにはどうすればいいのだろう
LLVM IRの後ろの方に`attributes #0`が存在している
方法
clang=front-endがMultiCanaryのAllocaInstを作ってサイズを設定して、LLVMがそれに従う
なんか微妙
実証は楽?そうでもない?
clangからはCanaryのサイズだけ伝わるようにして、LLVMで完結させる→こっちを利用
parameter attributes
LLVM IRの構文を拡張して、その拡張をclangから使えるようにする必要がある
[How to add an attribute https://clang.llvm.org/docs/InternalsManual.html#how-to-add-an-attribute] ドンピシャなガイドがある
IgnoredAttrは他のコンパイラでは実装されてるけどclangでは無視したい、といったときに使う
>An attribute that inherits from IgnoredAttr is parsed, but will generate an ignored attribute diagnostic when used, which may be useful when an attribute is supported by another vendor but not supported by clang.
code:Attr.rd
/// An ignored attribute, which we parse but discard with no checking.
class IgnoredAttr : Attr {
another vendor = CUDA とか
LLVMの拡張
[- Attributes→parameter attributes, function attributes, global attributes]
IRのlexer/parserにkw_multicanry_sizeを追加
allocainstの構文を拡張
MultiCanary Passからそれを利用してIRを書き換える
clangの拡張
__attribute__構文に追加
hasAttrを使ってDecl?に落とし込む
CGDecl.cpp:EmitAutoVarAllocaでgetAttrしてからAllocaInstにセット
あとはLLVMにおまかせ
なぜかMultiCanarySizeに値がセットできていない、`clang -emit-llvm`で`multicanary_size`を含んだIRが表示されない
[https://gyazo.com/d215c67c11c06ee6105d1083c34e7966]
CreateTempAllocaWithoutCastにMultiCanarySizeを渡し忘れていた…
バッファの型が`[256 x i32]`であるのに対してCanaryは`i8*`(=`i32`, `i64`)なのでそのまま比較できない
`alloca i8*, i32 32` vs `alloca [32 x i8*]`
GEPは`i8*`などのPointer Typeを要素にもつ配列には使えない…?
Instructions.cpp:1433
代わりに`ptrtoint`で
entryブロック内に(multicanary_size指定のある)allocaInstが2つあると、片方がsplitされて別のBasicBlockに移る→isKnownSentinelで落ちる
新しいブロックに移される=後からまたループで参照されることが保証されているので、はじめのallocaInstの処理が終わればcontinueしてしまえばよい
IRBuilderにはBBをまたいで後ろに挿入する癖がある?かと思ったら前にも入れるっぽいしよくわからん
code:MultiCanary.cpp
// ↓のようになるのでIRBuilder<> B(AI)は使えない
// %MultiCanaryLoad5 = load i8*, i8* addrspace(257)* inttoptr (i32 40 to i8* addrspace(257)*)
// %MultiCanaryAlloca6 = alloca i8*, i32 32
//
// ; <label>:0: ; preds = %MultiCanaryStoreLoopHead
// br label %MultiCanaryStore
//
// IRBuilder<> BBB(BB.getContext());
// BBB.CreateBr(StoreBB);
CreateBrは動いたり動かなかったりする
BranchInst::Createを使う
でも落ちる
code:SelectionDAGBuilder.cpp
AllocaInst *CanarySlot = cast<AllocaInst>(I.getArgOperand(1));
AllocaInst *BufferSlot = cast<AllocaInst>(I.getArgOperand(2));
int CanaryFI = FuncInfo.StaticAllocaMap[CanarySlot];
int BufferFI = FuncInfo.StaticAllocaMap[BufferSlot];
errs() << CanaryFI << " for " << BufferFI << "\n";
MFI.setMultiCanaryIndex(BufferFI, CanaryFI);
SDValue CanaryFIN = DAG.getFrameIndex(CanaryFI, PtrTy);
// Store the canary onto the stack.
Res = DAG.getStore(
Chain, sdl, Src, CanaryFIN,
MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), CanaryFI),
/* Alignment = */ 0, MachineMemOperand::MOVolatile);
CanarySlot->dump();
BufferSlot->dump();
errs() << FuncInfo.StaticAllocaMap.count(CanarySlot) << "\n";
errs() << FuncInfo.StaticAllocaMap.count(BufferSlot) << "\n";
if (FuncInfo.StaticAllocaMap.find(CanarySlot) == FuncInfo.StaticAllocaMap.end()) {
errs() << "CanarySlot not found in StaticAllocaMap\n";
}
if (FuncInfo.StaticAllocaMap.find(BufferSlot) == FuncInfo.StaticAllocaMap.end()) {
errs() << "BufferSlot not found in StaticAllocaMap\n";
}
Res->dump();
setValue(&I, Res);
code:attr.c
#include <stdio.h>
int main(int argc, char **argv) {
__attribute__((multicanary_size(16))) int hoge[256];
__attribute__((multicanary_size(128))) char huga[16];
__attribute__((multicanary_size(256))) char fuga[24];
return 0;
}
code:attr_opt_llc.txt
0 for 0 ; [MultiCanaryAllocaのStaticAllocaMap] for [BufferのStaticAllocaMap], Idx=0のCanaryをIdx=0のBufferに割り当てている???
%MultiCanaryAlloca15 = alloca i8*, i32 32 ; CanaryAI
%fuga = alloca [24 x i8], align 16, multicanary_size 256 ; BufferAI
1
1
t5: ch = store<(volatile store 8 into %fixed-stack.0)> t0, t2, FrameIndex:i64<0>, undef:i64
# *** IR Dump After X86 DAG->DAG Instruction Selection ***:
# Machine code for function main: IsSSA, TracksLiveness
Frame Objects:
fi#0: size=8, align=8, at location [SP+8]
fi#1: size=4, align=4, at location [SP+8]
fi#2: size=8, align=8, at location [SP+8]
fi#3: size=4, align=4, at location [SP+8]
fi#4: size=8, align=8, at location [SP+8]
fi#5: size=8, align=8, at location [SP+8]
fi#6: size=16, align=8, at location [SP+8]
fi#7: size=1024, align=16, at location [SP+8] ; %hugaのみ確保されている
fi#8: variable sized, align=1, at location [SP+8]
fi#9: variable sized, align=1, at location [SP+8]
...
; MI->dump()させた↓
%44:gr64 = MOV64rm %2:gr64, 1, $noreg, 0, $noreg :: (volatile load 8 from %ir.MultiCanaryAlloca8)
llc: /home/sei0o/Devs/llvm/lib/CodeGen/LiveVariables.cpp:134: void llvm::LiveVariables::HandleVirtRegUse(unsigned int, llvm::MachineBasicBlock*, llvm::MachineInstr&): Assertion `MRI->getVRegDef(reg) && "Register use before def!"' failed.
Stack dump:
`MultiCanaryAlloca8`を宣言する前にロードしている
直前のMIRにそもそもMulCanaryAlloca / `%ir.MultiCanaryAlloca8`が存在せず、SelectionDAGBuilderの後で消滅している
はて??
`attr.c`をいろいろ変化させてみるも同じ
StaticAllocaMapに同じ値(`0`)がセットさせているのがよくなさそう
代入しているところを見てみる
code:FunctionLoweringInfo.cpp
if (Iter != CatchObjects.end() && TLI->needsFixedCatchObjects()) { ; Windows + EH???
FrameIndex = MF->getFrameInfo().CreateFixedObject(
TySize, 0, /*Immutable=*/false, /*isAliased=*/true);
MF->getFrameInfo().setObjectAlignment(FrameIndex, Align);
} else { ; こちら側を通る
FrameIndex =
MF->getFrameInfo().CreateStackObject(TySize, Align, false, AI);
}
StaticAllocaMap[AI] = FrameIndex;
`CreateFixedObject`ではNumFixedObjectsが更新されている
`CreateStackObject`では更新されている感じはない
`MachineFrameInfo::setMultiCanaryIndex`あたりは`NumFixedObjects`に依存している
しかし他のメンバ変数・関数もそうなので関係なさそう
というかMultiCanaryはそこを通っていない?→そんなことなかった
いろいろ探してみると、FunctionLoweringInfo.cpp:132で`MultiCanaryAlloca8`だけStatic扱いされてない(???)
code:gdb
gdb-peda$ p AI->isStaticAlloca()
$41 = 0x0
gdb-peda$ p AI->getName()
$42 = {
static npos = 0xffffffffffffffff,
Data = 0x55555c425550 "MultiCanaryAlloca8",
Length = 0x12
}
code:Instructions.cpp
/// isStaticAlloca - Return true if this alloca is in the entry block of the
/// function and is a constant size. If so, the code generator will fold it
/// into the prolog/epilog code, so it is basically free.
bool AllocaInst::isStaticAlloca() const {
ここの`free`は計算量がO(1)って意味?
if this alloca is in the entry block
<span title='There is no note that matches this link.' class='invalid-link'> <span class='invalid-link-brackets'>[[</span> if this alloca is in the entry block <span class='invalid-link-brackets'>]]</span></span>
[** if this alloca is in the entry block]
[**** if this alloca is in the entry block] マジかよ〜
BasicBlock分けてCanary設置済みかそうでないかを識別するアプローチはダメってことか
でもcallはOKなんだよな
Before: alloca Buffer→alloca Canary→CanaryStoreBB→llvm.multicanary→別のbuffer→...
After: alloca all Buffer + all Canary→llvm.multicanary→lots of CanaryStoreBB
entry basicblockにすべてを収められる
が、よくよく考えたらentry basicblockにはalloca以降の処理も置かれているので、entryの後ろに処理を挿入するとプログラムが終わってからcanaryを挿入することになってしまう(それはダメでしょう)
よく見るとAllocaInstはentryの前半に連続して置かれているので、allocaの連続が終わったらそこからは実際の処理だとみなして切ってしまう
その仮定大丈夫?
FailBBがなぜか消えている
単純なミスだった
Backlinks
SecHack365
SecHack365は学生(と社会人)が、1年間指導を受けながらセキュリティに関連したり関連しなかったりするテーマで何かをつくる長期ハッカソン。無料で、交通費も全部出る。筆者は2018年度に、「ライブラリ・リンカ・ローダ・コンパイラetcを連携させたセキュリティ機能の開発」というテーマで参加した。