https://github.com/gbMattN updated https://github.com/llvm/llvm-project/pull/227782
>From a85f00e569fdaa114b9275509aa571c73f32709b Mon Sep 17 00:00:00 2001 From: gbMattN <[email protected]> Date: Wed, 30 Sep 2026 17:07:22 +0100 Subject: [PATCH 1/5] [TySan] Let TypeSanitizer know about conservative path TBAA data --- clang/lib/CodeGen/CodeGenTBAA.cpp | 3 ++ compiler-rt/lib/tysan/tysan.cpp | 22 +++++++++++++-- .../tysan/no-false-positive-issue208646.cpp | 11 ++++++++ .../tysan/no-false-positive-issue208647.cpp | 10 +++++++ .../tysan/no-false-positive-issue208655.cpp | 15 ++++++++++ .../tysan/no-false-positive-issue210643.cpp | 11 ++++++++ .../Instrumentation/TypeSanitizer.cpp | 28 ++++++++++++++++++- 7 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 compiler-rt/test/tysan/no-false-positive-issue208646.cpp create mode 100644 compiler-rt/test/tysan/no-false-positive-issue208647.cpp create mode 100644 compiler-rt/test/tysan/no-false-positive-issue208655.cpp create mode 100644 compiler-rt/test/tysan/no-false-positive-issue210643.cpp diff --git a/clang/lib/CodeGen/CodeGenTBAA.cpp b/clang/lib/CodeGen/CodeGenTBAA.cpp index 1854df7c7c0f1..acdf6abc59b61 100644 --- a/clang/lib/CodeGen/CodeGenTBAA.cpp +++ b/clang/lib/CodeGen/CodeGenTBAA.cpp @@ -367,6 +367,9 @@ llvm::MDNode *CodeGenTBAA::getTypeInfoHelper(const Type *Ty) { } // For now, handle any other kind of type conservatively. + if(Features.Sanitize.has(SanitizerKind::Type)){ + return createScalarTypeNode("TysanConservativeTBAA", getChar(), 1); + } return getChar(); } diff --git a/compiler-rt/lib/tysan/tysan.cpp b/compiler-rt/lib/tysan/tysan.cpp index 8b3b60dc4cf83..cc638cef5db83 100644 --- a/compiler-rt/lib/tysan/tysan.cpp +++ b/compiler-rt/lib/tysan/tysan.cpp @@ -128,12 +128,27 @@ static tysan_type_descriptor *getRootTD(tysan_type_descriptor *TD) { return RootTD; } +// Currently, Clang's TBAA system does not correctly describe every possible +// type. For unhandled types, it makes the most conservative choice, emitting +// omnipotent char. TySan needs to handle this seperately to a real omnipotent +// char otherwise the user may get false positives. When compiling with TySan +// enabled, clang will emit a special TBAA type to show that the conservative +// path has been taken. When the transformation pass finds this TBAA, it will +// set this global variable. This then allows quick comparison of TDs at +// runtime. +static tysan_type_descriptor *__tysan_conservative_tbaa_descriptor = nullptr; +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void +__tysan_set_conservative_tbaa_descriptor( + tysan_type_descriptor *conservativeTBAATD) { + __tysan_conservative_tbaa_descriptor = conservativeTBAATD; +} + // Walk up TDA to see if it reaches TDB. static bool walkAliasTree(tysan_type_descriptor *TDA, tysan_type_descriptor *TDB, uptr OffsetA, uptr OffsetB) { do { - if (TDA == TDB) + if (TDA == TDB || TDA == __tysan_conservative_tbaa_descriptor) return OffsetA == OffsetB; if (TDA->Tag == TYSAN_STRUCT_TD) { @@ -182,7 +197,6 @@ static bool isAliasingLegalUp(tysan_type_descriptor *TDA, OffsetA = TDA->Member.Offset; TDA = TDA->Member.Base; } - return walkAliasTree(TDA, TDB, OffsetA, OffsetB); } @@ -213,7 +227,9 @@ static bool isAliasingLegalWithOffset(tysan_type_descriptor *TDA, static bool isAliasingLegal(tysan_type_descriptor *TDA, tysan_type_descriptor *TDB, uptr OffsetB = 0) { - if (TDA == TDB || !TDB || !TDA) + if (TDA == TDB || !TDB || !TDA || + TDA == __tysan_conservative_tbaa_descriptor || + TDB == __tysan_conservative_tbaa_descriptor) return true; // Aliasing is legal is the two types have different root nodes. diff --git a/compiler-rt/test/tysan/no-false-positive-issue208646.cpp b/compiler-rt/test/tysan/no-false-positive-issue208646.cpp new file mode 100644 index 0000000000000..236b4a86d0a2b --- /dev/null +++ b/compiler-rt/test/tysan/no-false-positive-issue208646.cpp @@ -0,0 +1,11 @@ +// RUN: %clangxx_tysan -O0 %s -o %t && %run %t + +#include <string> +#include <optional> + +static std::optional<std::string> optional_var = std::nullopt; + +int main() { + optional_var = "this is a random long string (short one does not reproduce)"; + return 0; +} diff --git a/compiler-rt/test/tysan/no-false-positive-issue208647.cpp b/compiler-rt/test/tysan/no-false-positive-issue208647.cpp new file mode 100644 index 0000000000000..ef8b004496a16 --- /dev/null +++ b/compiler-rt/test/tysan/no-false-positive-issue208647.cpp @@ -0,0 +1,10 @@ +// RUN: %clangxx_tysan -O0 %s -o %t && %run %t + +#include <variant> + +int main() { + std::variant<int, double> v; + v = 1; + v = 3.5; + return 0; +} diff --git a/compiler-rt/test/tysan/no-false-positive-issue208655.cpp b/compiler-rt/test/tysan/no-false-positive-issue208655.cpp new file mode 100644 index 0000000000000..7518b244513cd --- /dev/null +++ b/compiler-rt/test/tysan/no-false-positive-issue208655.cpp @@ -0,0 +1,15 @@ +// RUN: %clangxx_tysan -O0 %s -o %t && %run %t + +#include <vector> + +struct Registry { + std::vector<int> arr; + char temp_byte; + bool bool_var = false; +}; + +int main() { + static Registry r; + r.arr.push_back(0); + r.bool_var = true; +} diff --git a/compiler-rt/test/tysan/no-false-positive-issue210643.cpp b/compiler-rt/test/tysan/no-false-positive-issue210643.cpp new file mode 100644 index 0000000000000..d667f75646cfd --- /dev/null +++ b/compiler-rt/test/tysan/no-false-positive-issue210643.cpp @@ -0,0 +1,11 @@ +// RUN: %clangxx_tysan -O0 %s -o %t && %run %t + +struct A { + int elems[3]; +}; + +A a; + +int main() { + a.elems[0] = 1; +} diff --git a/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp index c6436617d69e6..4aab250a32491 100644 --- a/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp @@ -80,6 +80,7 @@ struct TypeSanitizer { TypeSanitizer(Module &M); bool sanitizeFunction(Function &F, const TargetLibraryInfo &TLI); void instrumentGlobals(Module &M); + void setConservativeTBAA(Module &M); private: typedef SmallDenseMap<const MDNode *, GlobalVariable *, 8> @@ -127,6 +128,9 @@ struct TypeSanitizer { FunctionCallee TysanInstrumentWithShadowUpdate; FunctionCallee TysanSetShadowType; + FunctionCallee TysanSetConservativeTBAADescriptor; + GlobalVariable *ConservativeTBAADescriptor; + /// Callback to set types for gloabls. Function *TysanGlobalsSetTypeFunction; }; @@ -134,7 +138,8 @@ struct TypeSanitizer { TypeSanitizer::TypeSanitizer(Module &M) : TargetTriple(M.getTargetTriple()), - AnonNameRegex("^_ZTS.*N[1-9][0-9]*_GLOBAL__N") { + AnonNameRegex("^_ZTS.*N[1-9][0-9]*_GLOBAL__N"), + ConservativeTBAADescriptor(nullptr) { const DataLayout &DL = M.getDataLayout(); IntptrTy = DL.getIntPtrType(M.getContext()); PtrShift = countr_zero(IntptrTy->getPrimitiveSizeInBits() / 8); @@ -199,6 +204,12 @@ void TypeSanitizer::initializeCallbacks(Module &M) { IRB.getPtrTy(), // Pointer to the new type descriptor U64Ty // Size of data we access in bytes ); + + TysanSetConservativeTBAADescriptor = M.getOrInsertFunction( + "__tysan_set_conservative_tbaa_descriptor", Attr, IRB.getVoidTy(), + IRB.getPtrTy() // Pointer to the type descriptor that describes the TBAA + // clang generates under the conservative TBAA path + ); } void TypeSanitizer::instrumentGlobals(Module &M) { @@ -247,6 +258,16 @@ void TypeSanitizer::instrumentGlobals(Module &M) { } } +void TypeSanitizer::setConservativeTBAA(Module &M) { + if (ConservativeTBAADescriptor) { + IRBuilder<> IRB(cast<Function>(TysanCtorFunction.getCallee()) + ->getEntryBlock() + .getTerminator()); + IRB.CreateCall(TysanSetConservativeTBAADescriptor, + {ConservativeTBAADescriptor}); + } +} + static const char LUT[] = "0123456789abcdef"; static std::string encodeName(StringRef Name) { @@ -404,6 +425,9 @@ bool TypeSanitizer::generateBaseTypeDescriptor( TD, EncodedName); M.insertGlobalVariable(TDGV); + if (Name == "TysanConservativeTBAA") { + ConservativeTBAADescriptor = TDGV; + } if (ShouldBeComdat) { if (TargetTriple.isOSBinFormatELF()) { Comdat *TDComdat = M.getOrInsertComdat(EncodedName); @@ -986,5 +1010,7 @@ PreservedAnalyses TypeSanitizerPass::run(Module &M, } } + TySan.setConservativeTBAA(M); + return PreservedAnalyses::none(); } >From 567c2e02b146041a94edb536a64fc078e4b07e83 Mon Sep 17 00:00:00 2001 From: gbMattN <[email protected]> Date: Wed, 30 Sep 2026 17:52:42 +0100 Subject: [PATCH 2/5] Format repro's copied from issues --- .../test/tysan/no-false-positive-issue208646.cpp | 4 ++-- .../test/tysan/no-false-positive-issue208655.cpp | 12 ++++++------ .../test/tysan/no-false-positive-issue210643.cpp | 6 ++---- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/compiler-rt/test/tysan/no-false-positive-issue208646.cpp b/compiler-rt/test/tysan/no-false-positive-issue208646.cpp index 236b4a86d0a2b..fc8a2da6e8b11 100644 --- a/compiler-rt/test/tysan/no-false-positive-issue208646.cpp +++ b/compiler-rt/test/tysan/no-false-positive-issue208646.cpp @@ -1,11 +1,11 @@ // RUN: %clangxx_tysan -O0 %s -o %t && %run %t -#include <string> #include <optional> +#include <string> static std::optional<std::string> optional_var = std::nullopt; -int main() { +int main() { optional_var = "this is a random long string (short one does not reproduce)"; return 0; } diff --git a/compiler-rt/test/tysan/no-false-positive-issue208655.cpp b/compiler-rt/test/tysan/no-false-positive-issue208655.cpp index 7518b244513cd..cba71c5b5be49 100644 --- a/compiler-rt/test/tysan/no-false-positive-issue208655.cpp +++ b/compiler-rt/test/tysan/no-false-positive-issue208655.cpp @@ -3,13 +3,13 @@ #include <vector> struct Registry { - std::vector<int> arr; - char temp_byte; - bool bool_var = false; + std::vector<int> arr; + char temp_byte; + bool bool_var = false; }; int main() { - static Registry r; - r.arr.push_back(0); - r.bool_var = true; + static Registry r; + r.arr.push_back(0); + r.bool_var = true; } diff --git a/compiler-rt/test/tysan/no-false-positive-issue210643.cpp b/compiler-rt/test/tysan/no-false-positive-issue210643.cpp index d667f75646cfd..4a74abd43d02d 100644 --- a/compiler-rt/test/tysan/no-false-positive-issue210643.cpp +++ b/compiler-rt/test/tysan/no-false-positive-issue210643.cpp @@ -1,11 +1,9 @@ // RUN: %clangxx_tysan -O0 %s -o %t && %run %t struct A { - int elems[3]; + int elems[3]; }; A a; -int main() { - a.elems[0] = 1; -} +int main() { a.elems[0] = 1; } >From 8678792148af588bc704cab42fb983cce69d359c Mon Sep 17 00:00:00 2001 From: gbMattN <[email protected]> Date: Thu, 1 Oct 2026 11:17:59 +0100 Subject: [PATCH 3/5] Replace global sentinal with new TD tag --- compiler-rt/lib/tysan/tysan.cpp | 23 +++---------- compiler-rt/lib/tysan/tysan.h | 2 +- .../Instrumentation/TypeSanitizer.cpp | 33 +++---------------- 3 files changed, 11 insertions(+), 47 deletions(-) diff --git a/compiler-rt/lib/tysan/tysan.cpp b/compiler-rt/lib/tysan/tysan.cpp index cc638cef5db83..26388e4e16ac7 100644 --- a/compiler-rt/lib/tysan/tysan.cpp +++ b/compiler-rt/lib/tysan/tysan.cpp @@ -119,6 +119,8 @@ static tysan_type_descriptor *getRootTD(tysan_type_descriptor *TD) { TD = nullptr; } else if (TD->Tag == TYSAN_MEMBER_TD) { TD = TD->Member.Access; + } else if (TD->Tag == TYSAN_CONSERVATIVE_ALIAS_TD) { + return RootTD; } else { CHECK(false && "invalid enum value"); break; @@ -128,27 +130,12 @@ static tysan_type_descriptor *getRootTD(tysan_type_descriptor *TD) { return RootTD; } -// Currently, Clang's TBAA system does not correctly describe every possible -// type. For unhandled types, it makes the most conservative choice, emitting -// omnipotent char. TySan needs to handle this seperately to a real omnipotent -// char otherwise the user may get false positives. When compiling with TySan -// enabled, clang will emit a special TBAA type to show that the conservative -// path has been taken. When the transformation pass finds this TBAA, it will -// set this global variable. This then allows quick comparison of TDs at -// runtime. -static tysan_type_descriptor *__tysan_conservative_tbaa_descriptor = nullptr; -extern "C" SANITIZER_INTERFACE_ATTRIBUTE void -__tysan_set_conservative_tbaa_descriptor( - tysan_type_descriptor *conservativeTBAATD) { - __tysan_conservative_tbaa_descriptor = conservativeTBAATD; -} - // Walk up TDA to see if it reaches TDB. static bool walkAliasTree(tysan_type_descriptor *TDA, tysan_type_descriptor *TDB, uptr OffsetA, uptr OffsetB) { do { - if (TDA == TDB || TDA == __tysan_conservative_tbaa_descriptor) + if (TDA == TDB || TDA->Tag == TYSAN_CONSERVATIVE_ALIAS_TD) return OffsetA == OffsetB; if (TDA->Tag == TYSAN_STRUCT_TD) { @@ -228,8 +215,8 @@ static bool isAliasingLegalWithOffset(tysan_type_descriptor *TDA, static bool isAliasingLegal(tysan_type_descriptor *TDA, tysan_type_descriptor *TDB, uptr OffsetB = 0) { if (TDA == TDB || !TDB || !TDA || - TDA == __tysan_conservative_tbaa_descriptor || - TDB == __tysan_conservative_tbaa_descriptor) + TDA->Tag == TYSAN_CONSERVATIVE_ALIAS_TD || + TDB->Tag == TYSAN_CONSERVATIVE_ALIAS_TD) return true; // Aliasing is legal is the two types have different root nodes. diff --git a/compiler-rt/lib/tysan/tysan.h b/compiler-rt/lib/tysan/tysan.h index 791c6a47ce5f8..8ac957250b002 100644 --- a/compiler-rt/lib/tysan/tysan.h +++ b/compiler-rt/lib/tysan/tysan.h @@ -33,7 +33,7 @@ extern bool tysan_init_is_running; void InitializeInterceptors(); -enum { TYSAN_MEMBER_TD = 1, TYSAN_STRUCT_TD = 2 }; +enum { TYSAN_MEMBER_TD = 1, TYSAN_STRUCT_TD = 2, TYSAN_CONSERVATIVE_ALIAS_TD = 3 }; struct tysan_member_type_descriptor { struct tysan_type_descriptor *Base; diff --git a/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp index 4aab250a32491..43a5ae400d960 100644 --- a/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp @@ -80,7 +80,6 @@ struct TypeSanitizer { TypeSanitizer(Module &M); bool sanitizeFunction(Function &F, const TargetLibraryInfo &TLI); void instrumentGlobals(Module &M); - void setConservativeTBAA(Module &M); private: typedef SmallDenseMap<const MDNode *, GlobalVariable *, 8> @@ -128,9 +127,6 @@ struct TypeSanitizer { FunctionCallee TysanInstrumentWithShadowUpdate; FunctionCallee TysanSetShadowType; - FunctionCallee TysanSetConservativeTBAADescriptor; - GlobalVariable *ConservativeTBAADescriptor; - /// Callback to set types for gloabls. Function *TysanGlobalsSetTypeFunction; }; @@ -138,8 +134,7 @@ struct TypeSanitizer { TypeSanitizer::TypeSanitizer(Module &M) : TargetTriple(M.getTargetTriple()), - AnonNameRegex("^_ZTS.*N[1-9][0-9]*_GLOBAL__N"), - ConservativeTBAADescriptor(nullptr) { + AnonNameRegex("^_ZTS.*N[1-9][0-9]*_GLOBAL__N") { const DataLayout &DL = M.getDataLayout(); IntptrTy = DL.getIntPtrType(M.getContext()); PtrShift = countr_zero(IntptrTy->getPrimitiveSizeInBits() / 8); @@ -204,12 +199,6 @@ void TypeSanitizer::initializeCallbacks(Module &M) { IRB.getPtrTy(), // Pointer to the new type descriptor U64Ty // Size of data we access in bytes ); - - TysanSetConservativeTBAADescriptor = M.getOrInsertFunction( - "__tysan_set_conservative_tbaa_descriptor", Attr, IRB.getVoidTy(), - IRB.getPtrTy() // Pointer to the type descriptor that describes the TBAA - // clang generates under the conservative TBAA path - ); } void TypeSanitizer::instrumentGlobals(Module &M) { @@ -258,16 +247,6 @@ void TypeSanitizer::instrumentGlobals(Module &M) { } } -void TypeSanitizer::setConservativeTBAA(Module &M) { - if (ConservativeTBAADescriptor) { - IRBuilder<> IRB(cast<Function>(TysanCtorFunction.getCallee()) - ->getEntryBlock() - .getTerminator()); - IRB.CreateCall(TysanSetConservativeTBAADescriptor, - {ConservativeTBAADescriptor}); - } -} - static const char LUT[] = "0123456789abcdef"; static std::string encodeName(StringRef Name) { @@ -398,7 +377,10 @@ bool TypeSanitizer::generateBaseTypeDescriptor( TDSubData.push_back(C); }; - PushTDSub(ConstantInt::get(IntptrTy, 2)); + int TDTag = 2; // 2 is for structs + if (Name == "TysanConservativeTBAA") + TDTag = 3; // 3 is for TDs that conservatively alias with everything + PushTDSub(ConstantInt::get(IntptrTy, TDTag)); PushTDSub(ConstantInt::get(IntptrTy, Members.size())); // Types that are in an anonymous namespace are local to this module. @@ -425,9 +407,6 @@ bool TypeSanitizer::generateBaseTypeDescriptor( TD, EncodedName); M.insertGlobalVariable(TDGV); - if (Name == "TysanConservativeTBAA") { - ConservativeTBAADescriptor = TDGV; - } if (ShouldBeComdat) { if (TargetTriple.isOSBinFormatELF()) { Comdat *TDComdat = M.getOrInsertComdat(EncodedName); @@ -1010,7 +989,5 @@ PreservedAnalyses TypeSanitizerPass::run(Module &M, } } - TySan.setConservativeTBAA(M); - return PreservedAnalyses::none(); } >From ab021295efd198650921612d183583dd636b5f72 Mon Sep 17 00:00:00 2001 From: gbMattN <[email protected]> Date: Thu, 1 Oct 2026 11:38:55 +0100 Subject: [PATCH 4/5] Formatting --- clang/lib/CodeGen/CodeGenTBAA.cpp | 2 +- compiler-rt/lib/tysan/tysan.cpp | 3 +-- compiler-rt/lib/tysan/tysan.h | 6 +++++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/clang/lib/CodeGen/CodeGenTBAA.cpp b/clang/lib/CodeGen/CodeGenTBAA.cpp index acdf6abc59b61..166ef249353ef 100644 --- a/clang/lib/CodeGen/CodeGenTBAA.cpp +++ b/clang/lib/CodeGen/CodeGenTBAA.cpp @@ -367,7 +367,7 @@ llvm::MDNode *CodeGenTBAA::getTypeInfoHelper(const Type *Ty) { } // For now, handle any other kind of type conservatively. - if(Features.Sanitize.has(SanitizerKind::Type)){ + if (Features.Sanitize.has(SanitizerKind::Type)) { return createScalarTypeNode("TysanConservativeTBAA", getChar(), 1); } return getChar(); diff --git a/compiler-rt/lib/tysan/tysan.cpp b/compiler-rt/lib/tysan/tysan.cpp index 26388e4e16ac7..7322b67a8eff5 100644 --- a/compiler-rt/lib/tysan/tysan.cpp +++ b/compiler-rt/lib/tysan/tysan.cpp @@ -214,8 +214,7 @@ static bool isAliasingLegalWithOffset(tysan_type_descriptor *TDA, static bool isAliasingLegal(tysan_type_descriptor *TDA, tysan_type_descriptor *TDB, uptr OffsetB = 0) { - if (TDA == TDB || !TDB || !TDA || - TDA->Tag == TYSAN_CONSERVATIVE_ALIAS_TD || + if (TDA == TDB || !TDB || !TDA || TDA->Tag == TYSAN_CONSERVATIVE_ALIAS_TD || TDB->Tag == TYSAN_CONSERVATIVE_ALIAS_TD) return true; diff --git a/compiler-rt/lib/tysan/tysan.h b/compiler-rt/lib/tysan/tysan.h index 8ac957250b002..f279ad795a2ff 100644 --- a/compiler-rt/lib/tysan/tysan.h +++ b/compiler-rt/lib/tysan/tysan.h @@ -33,7 +33,11 @@ extern bool tysan_init_is_running; void InitializeInterceptors(); -enum { TYSAN_MEMBER_TD = 1, TYSAN_STRUCT_TD = 2, TYSAN_CONSERVATIVE_ALIAS_TD = 3 }; +enum { + TYSAN_MEMBER_TD = 1, + TYSAN_STRUCT_TD = 2, + TYSAN_CONSERVATIVE_ALIAS_TD = 3 +}; struct tysan_member_type_descriptor { struct tysan_type_descriptor *Base; >From 21ad1da14ecd69da966165a3ad07234b0b911b8a Mon Sep 17 00:00:00 2001 From: gbMattN <[email protected]> Date: Thu, 1 Oct 2026 11:43:42 +0100 Subject: [PATCH 5/5] nit. line --- compiler-rt/lib/tysan/tysan.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler-rt/lib/tysan/tysan.cpp b/compiler-rt/lib/tysan/tysan.cpp index 7322b67a8eff5..30cf9bf495a8d 100644 --- a/compiler-rt/lib/tysan/tysan.cpp +++ b/compiler-rt/lib/tysan/tysan.cpp @@ -184,6 +184,7 @@ static bool isAliasingLegalUp(tysan_type_descriptor *TDA, OffsetA = TDA->Member.Offset; TDA = TDA->Member.Base; } + return walkAliasTree(TDA, TDB, OffsetA, OffsetB); } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
