ClickHouse: CHJIT::compileModule failed with use-of-uninitialized-value in stress tests

MemorySanitizer: use-of-uninitialized-value
    #0 0x49c05839 in llvm::iplist_impl<llvm::simple_ilist<llvm::MachineInstr, llvm::ilist_sentinel_tracking<true>>, llvm::ilist_traits<llvm::MachineInstr>>::~iplist_impl() MachineBasicBlock.cpp
    #1 0x49c05c5f in llvm::MachineBasicBlock::~MachineBasicBlock() (/usr/bin/clickhouse+0x49c05c5f) (BuildId: 
    ...
    #13 0x41784edf in DB::JITCompiler::compile(llvm::Module&) build_docker/../src/Interpreters/JIT/CHJIT.cpp:78:22
    #14 0x4178047d in DB::CHJIT::compileModule(std::__1::unique_ptr<llvm::Module, std::__1::default_delete<llvm::Module>>) build_docker/../src/Interpreters/JIT/CHJIT.cpp:378:29
    #15 0x4177f6ed in DB::CHJIT::compileModule(std::__1::function<void (llvm::Module&)>) build_docker/../src/Interpreters/JIT/CHJIT.cpp:359:24
    ...

play ci

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 15 (10 by maintainers)

Most upvoted comments

After investigation there is bug detected with memory sanitized in LLVM-12 that I cannot reproduce in LLVM-15. Compilation example:

static void compileAddIntoAggregateStatesFunctions(llvm::Module & module, const std::string & name)
{
    llvm::IRBuilder<> b(module.getContext());

    auto * size_type = b.getIntNTy(sizeof(size_t) * 8);
    llvm::Type * places_type = b.getInt8Ty()->getPointerTo()->getPointerTo();

    auto * add_into_aggregate_states_func_declaration = llvm::FunctionType::get(b.getVoidTy(), { size_type, size_type, places_type }, false);
    auto * add_into_aggregate_states_func = llvm::Function::Create(add_into_aggregate_states_func_declaration, llvm::Function::ExternalLinkage, name, module);

    auto * arguments = add_into_aggregate_states_func->args().begin();
    llvm::Value * row_start_arg = arguments++;
    llvm::Value * row_end_arg = arguments++;
    llvm::Value * places_arg = arguments++;

    auto * entry = llvm::BasicBlock::Create(b.getContext(), "entry", add_into_aggregate_states_func);
    b.SetInsertPoint(entry);

    auto * end = llvm::BasicBlock::Create(b.getContext(), "end", add_into_aggregate_states_func);
    auto * loop = llvm::BasicBlock::Create(b.getContext(), "loop", add_into_aggregate_states_func);

    b.CreateCondBr(b.CreateICmpEQ(row_start_arg, row_end_arg), end, loop);
    b.SetInsertPoint(loop);

    auto * counter_phi = b.CreatePHI(row_start_arg->getType(), 2);
    counter_phi->addIncoming(row_start_arg, entry);

    llvm::Value * aggregation_place = b.CreateLoad(b.getInt8Ty()->getPointerTo(), b.CreateGEP(b.getInt8Ty()->getPointerTo(), places_arg, counter_phi));
    b.CreateStore(llvm::ConstantInt::get(b.getInt8Ty(), 0), aggregation_place);

    auto * current_block = b.GetInsertBlock();
    auto * value = b.CreateAdd(counter_phi, llvm::ConstantInt::get(size_type, 1));
    counter_phi->addIncoming(value, current_block);
    b.CreateCondBr(b.CreateICmpEQ(value, row_end_arg), end, loop);

    b.SetInsertPoint(end);
    b.CreateRetVoid();
}

Result IR:

; ModuleID = 'jit0'
source_filename = "jit0"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

define void @"count()() 0 _add"(i64 %0, i64 %1, { i8*, i8* }* %2, i8** %3) {
entry:
  %4 = icmp eq i64 %0, %1
  br i1 %4, label %end, label %loop

end:                                              ; preds = %loop, %entry
  ret void

loop:                                             ; preds = %loop, %entry
  %5 = phi i64 [ %0, %entry ], [ %12, %loop ]
  %6 = getelementptr i8*, i8** %3, i64 %5
  %7 = load i8*, i8** %6, align 8
  %8 = getelementptr inbounds i8, i8* %7, i64 0
  %9 = bitcast i8* %8 to i64*
  %10 = load i64, i64* %9, align 8
  %11 = add i64 %10, 1
  store i64 %11, i64* %9, align 8
  %12 = add i64 %5, 1
  %13 = icmp eq i64 %12, %1
  br i1 %13, label %end, label %loop
}

Such IR generate memory sanitizer warning use-of-uninitialized-value somewhere in MachineBlock destructor. In can be potentially fixed in LLVM-12, trying to rewrite loop using other strategies but it does not look like a proper solution. In LLVM-15 generated IR (with OpaquePointers) would look like this:

CHJIT::compileModule module after optimization passes dump
; ModuleID = 'jit0'
source_filename = "jit0"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

; Function Attrs: nofree norecurse nosync nounwind
define void @add_into_aggregate_states(i64 %0, i64 %1, ptr nocapture readonly %2) local_unnamed_addr #0 {
entry:
  %3 = icmp eq i64 %0, %1
  br i1 %3, label %end, label %loop

end:                                              ; preds = %loop, %entry
  ret void

loop:                                             ; preds = %entry, %loop
  %4 = phi i64 [ %7, %loop ], [ %0, %entry ]
  %5 = getelementptr ptr, ptr %2, i64 %4
  %6 = load ptr, ptr %5, align 8
  store i8 0, ptr %6, align 1
  %7 = add i64 %4, 1
  %8 = icmp eq i64 %7, %1
  br i1 %8, label %end, label %loop
}

attributes #0 = { nofree norecurse nosync nounwind }

And it does not produce any memory sanitizer warnings during compilation. I checked other memory sanitizer crashes, and all of them can be fixed with LLVM 15 migration.