You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

LLVM函数Pass替换read为klee_make_symbolic的报错与崩溃问题

解决LLVM Pass替换read为klee_make_symbolic的两个核心问题

我来帮你拆解你遇到的这两个问题——它们分别对应LLVM IR的作用域规则和KLEE符号化函数的调用细节,咱们一步步解决:

第一个问题:"Referring to an argument in another function"报错

这个错误的根源很明确:你错误引用了read函数的形参,而非当前调用指令的实参。

LLVM中,每个函数的Argument对象属于该函数的作用域,你不能直接把read函数定义里的getArg(1)(比如buffer形参)拿来给klee_make_symbolic用——这相当于跨函数引用局部变量,LLVM的验证器会直接报错终止编译。

正确的做法是从**read的调用指令(CallInst)**中提取实参:

// 错误示例:引用了read函数的形参
Function* readFunc = M.getFunction("read");
Value* buffer = readFunc->getArg(1); 

// 正确示例:从当前CallInst中取实参
CallInst* ci = dyn_cast<CallInst>(&I);
Value* buffer = ci->getArgOperand(1); // 取read调用的第二个实参(buffer)

第二个问题:插桩后程序崩溃,手动替换却正常

虽然IR看起来无异常,但崩溃大概率是类型不匹配或符号化参数的细节处理不到位导致的,以下是排查和修复要点:

1. 严格匹配klee_make_symbolic的函数签名

klee_make_symbolic的C原型是:

void klee_make_symbolic(void *addr, size_t nbytes, const char *name);

对应LLVM IR的类型要求:

  • 第一个参数:必须是i8*(void*的LLVM等价类型)
  • 第二个参数:必须是i64(64位系统下size_t的类型)
  • 第三个参数:必须是指向全局字符串常量的i8*

如果你直接传递read的原始参数,很可能出现类型不兼容:

  • read的buffer可能是i32*/i64*等其他指针类型,需要转成i8*
  • read的size参数可能是i32,需要零扩展为i64

2. 正确创建符号名称的全局字符串

KLEE要求第三个参数是一个有效的字符串常量指针,如果你随便传一个局部指针或未初始化的值,会直接导致崩溃。必须创建全局字符串常量并做bitcast转换:

// 创建符号名称的全局字符串
std::string symName = "sym_buffer_" + std::to_string(ci->getDebugLoc().getLine());
Constant* symStr = ConstantDataArray::getString(M.getContext(), symName);
GlobalVariable* symStrGV = new GlobalVariable(
    M, symStr->getType(), true, GlobalValue::PrivateLinkage, symStr, ".str"
);
Value* symStrPtr = ConstantExpr::getBitCast(symStrGV, Type::getInt8PtrTy(M.getContext()));

3. 完整的正确Pass代码片段

结合以上要点,修复后的核心代码如下:

bool runOnFunction(Function &F) override {
    bool modified = false;
    Module &M = *F.getParent();

    // 声明klee_make_symbolic函数
    FunctionType* kleeFuncType = FunctionType::get(
        Type::getVoidTy(M.getContext()),
        {Type::getInt8PtrTy(M.getContext()),
         Type::getInt64Ty(M.getContext()),
         Type::getInt8PtrTy(M.getContext())},
        false
    );
    FunctionCallee kleeFunc = M.getOrInsertFunction("klee_make_symbolic", kleeFuncType);

    // 遍历所有指令
    for (auto &BB : F) {
        for (auto I = BB.begin(); I != BB.end();) {
            CallInst* ci = dyn_cast<CallInst>(&*I);
            ++I; // 先递增迭代器,避免删除指令后失效

            if (!ci) continue;
            Function* callee = ci->getCalledFunction();
            if (!callee || callee->getName() != "read") continue;

            // 提取read的实参
            Value* buffer = ci->getArgOperand(1);
            Value* size = ci->getArgOperand(2);

            // 转换buffer为i8*
            Value* bufferI8 = CastInst::CreatePointerCast(
                buffer, Type::getInt8PtrTy(M.getContext()), "buffer_i8", ci
            );

            // 转换size为i64(如果是i32的话)
            Value* sizeI64 = size;
            if (size->getType()->isIntegerTy(32)) {
                sizeI64 = CastInst::CreateZExt(
                    size, Type::getInt64Ty(M.getContext()), "size_i64", ci
                );
            }

            // 创建符号名称字符串
            std::string symName = "sym_" + F.getName().str() + "_" + std::to_string(ci->getDebugLoc().getLine());
            Constant* symStr = ConstantDataArray::getString(M.getContext(), symName);
            GlobalVariable* symStrGV = new GlobalVariable(
                M, symStr->getType(), true, GlobalValue::PrivateLinkage, symStr, ".sym_str"
            );
            Value* symStrPtr = ConstantExpr::getBitCast(symStrGV, Type::getInt8PtrTy(M.getContext()));

            // 插入klee_make_symbolic调用
            CallInst::Create(kleeFunc, {bufferI8, sizeI64, symStrPtr}, "", ci);

            // 删除原read调用
            ci->eraseFromParent();
            modified = true;
        }
    }
    return modified;
}

4. 额外排查步骤

如果还是崩溃,可以做这些检查:

  • 对比手动替换的IR和Pass生成的IR,逐行检查参数类型、字符串常量定义是否完全一致
  • 给KLEE添加-debug参数,查看崩溃时的调用栈和详细日志,定位具体出错环节
  • 确认原read调用已经被完全删除,避免程序先执行read再符号化导致内存状态混乱

内容的提问来源于stack exchange,提问作者funnypig run

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:20:38