MultiCanary Intrinsic Functionの実装
- Intrinsic Functionとして実装するのがやっぱりキレイ
- なぜ
alloca i8*
しているのかやっぱりわからない - FastISelを無効化してアセンブリに違いがあるか見てみよう→同じだった
%2 = alloca [38 x i8], align 16
%Canary = load volatile i8*, i8* addrspace(257)* inttoptr (i32 40 to i8* addrspace(257)*)
%CanaryCast = bitcast i8* %Canary to i64*
%CanaryX = ptrtoint i64* %CanaryCast to i64
%CanaryGEP = getelementptr inbounds [38 x i8], [38 x i8]* %2, i32 0, i32 30
%CanaryGEPT = bitcast i8* %CanaryGEP to i64*
store i64 %CanaryX, i64* %CanaryGEPT
// なぜalloca i8ではないのか?
%StackGuardSlot = alloca i8*
%StackGuard = load volatile i8*, i8* addrspace(257)* inttoptr (i32 40 to i8* addrspace(257)*)
call void @llvm.stackprotector(i8* %StackGuard, i8** %StackGuardSlot)
%23:gr64 = MOV64rm %noreg, 1, %noreg, 40, %fs; mem:Volatile LD8[inttoptr (i32 40 to i8* addrspace(257)*)(addrspace=257)] GR64:%23
MOV64mr %stack.0.StackGuardSlot, 1, %noreg, 0, %noreg, killed %23; GR64:%23
`i8* addrspace(257)*` = `(i8* addrspace(257)) *` = fsにいるi8*へのポインタ
`void*`がないので代わりに`i8*`を使っているだけか?
>Note that LLVM does not permit pointers to void (void*) nor does it permit pointers to labels (label*). Use i8* instead.
https://llvm.org/docs/LangRef.html#pointer-type
ポインタ型と同じサイズになっているからそのサイズぶん確保しているだけっぽい
libc_start_mainを追ってみると
code:glibc/csu/libc-start.c
STATIC int LIBC_START_MAIN (int (*main) (int, ...
/* Set up the stack checker's canary. */
uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (_dl_random);
# ifdef THREAD_SET_STACK_GUARD
THREAD_SET_STACK_GUARD (stack_chk_guard);
# else
__stack_chk_guard = stack_chk_guard;
# endif
...
code:glibc/sysdeps/unix/sysv/linux/dl-osinfo.h
static inline uintptr_t __attribute__ ((always_inline))
_dl_setup_stack_chk_guard (void *dl_random) {
union
{
uintptr_t num; // 定義がもうそうなっているのか
unsigned char bytes[sizeof (uintptr_t)];
} ret;
/* We need in the moment only 8 bytes on 32-bit platforms and 16
bytes on 64-bit platforms. Therefore we can use the data
directly and not use the kernel-provided data to seed a PRNG. */
memcpy (ret.bytes, dl_random, sizeof (ret));
...
return ret.num;
}
LocalStackSlotPass::calculateFrameObjectOffsets (LSS)
PrologEpilogInserter::calculateFrameObjectOffsets (PEI)
この2つでStackProtectorの処理が重複している
code:CodeGen/LocalStackSlotAllocation.cpp
// And tell MFI about it for PEI to use later
MFI.mapLocalFrameObject(FrameIdx, LocalOffset);
code:include/llvm/CodeGen/MachineFrameInfo.h
/// Map a frame index into the local object block
void mapLocalFrameObject(int ObjectIndex, int64_t Offset) {
LocalFrameObjects.push_back(std::pair<int, int64_t>(ObjectIndex, Offset));
Objects[ObjectIndex + NumFixedObjects].PreAllocated = true;
}
base registerが存在しない?ときにはLSSの情報は破棄されるのでPEIに書くほうがよさそう
よくわからん insertFrameReferenceRegisters
fixed stack?
>Note that any intrinsic using one of the llvm_any*_ty types for an argument or return type will be deemed by tblgen as overloaded and the corresponding suffix will be required on the intrinsic’s name.
[https://llvm.org/docs/ExtendingLLVM.html]
AllocaInstは任意の型オブジェクトへのポインタ型なので、llvm_any(ptr)_tyを使う必要がある
使用時には型も指定してあげる
code:MultiCanary.cpp
B.CreateCall(Intrinsic::getDeclaration(M, Intrinsic::multicanary, {AI->getType()}), {LI, CanaryAI, AI});
code:IR
%result = call i64 @llvm.read_register.i64(metadata !0) #5
code:twobuf_opt_llc.txt
5(canary) for 6
1 for 2
3 for 4
...
alloc FI(0) at SP[-24]
alloc FI(4) at SP[-64]
alloc FI(6) at SP[-96]
alloc FI(1) at SP[-104]
alloc FI(2) at SP[-108]
alloc FI(3) at SP[-120]
alloc FI(5) at SP[-128]
正しい場所に配置されてない
SelectionDAGからProlog/Epilog Insertionまでの間に、Objectが挿入されてIndexが変化したのが原因だった
他の関数を見ると`Objects[FI + NumFixedObjects]`のようにして取得している
なるほどね
Backlinks
SecHack365
SecHack365は学生(と社会人)が、1年間指導を受けながらセキュリティに関連したり関連しなかったりするテーマで何かをつくる長期ハッカソン。無料で、交通費も全部出る。筆者は2018年度に、「ライブラリ・リンカ・ローダ・コンパイラetcを連携させたセキュリティ機能の開発」というテーマで参加した。