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

从C代码调用Ruby正则表达式时崩溃,如何正确调用避免崩溃?

Ruby C API解析无效正则触发段错误的修复方案

这是此前帖子《Call to ruby regex through C api from C code not working》的续篇。修改代码后,调用rb_reg_regcomp处理正则字符串"*"时触发段错误,gdb捕获的崩溃栈信息如下:

Program received signal SIGSEGV, Segmentation fault.
0x000055555565bef6 in rb_ec_tag_jump (st=st@entry=RUBY_TAG_RAISE, ec=<optimized out>) at ../eval_intern.h:161
161     ec->tag->state = st;
(gdb) where
#0  0x000055555565bef6 in rb_ec_tag_jump (st=st@entry=RUBY_TAG_RAISE, ec=<optimized out>) at ../eval_intern.h:161
#1  0x0000555555661fe0 in rb_longjmp (ec=ec@entry=0x6160000000d0, tag=tag@entry=6, mesg=<optimized out>, mesg@entry=140737288676920, cause=<optimized out>, cause@entry=36) at ../eval.c:658
#2  0x000055555566231d in rb_exc_exception (mesg=mesg@entry=140737288676920, tag=tag@entry=6, cause=cause@entry=36) at ../vm_core.h:1866
#3  0x0000555555668628 in rb_exc_raise (mesg=mesg@entry=140737288676920) at ../eval.c:684
#4  0x00005555559387a5 in rb_reg_raise_str (err=<optimized out>, options=0, str=140737288677040) at ../re.c:3300
#5  rb_reg_init_str (options=0, s=140737288677040, re=140737288677000) at ../re.c:3300
#6  rb_reg_new_str (options=0, s=140737288677040) at ../re.c:3291
#7  rb_reg_regcomp (str=140737288677040) at ../re.c:3373
#8  0x000055555565aca1 in main () at ../eval.c:856

相关代码如下:

#include <ruby.h>
#include "ruby/re.h"

#define MAX_INPUT_SIZE 1000

int main(int argc, char** argv) {
    VALUE x;
    char string[MAX_INPUT_SIZE];
    int result;
    result = ruby_setup();
    ruby_init();

    ruby_init_loadpath();

    memset(string, 0, MAX_INPUT_SIZE);

    fgets(string, MAX_INPUT_SIZE, stdin);

    if (string[MAX_INPUT_SIZE-2]) {
        return 0;
    }

    x = rb_str_new_cstr("*");
    rb_reg_regcomp(x);

    return 0;
}

该代码用于模糊测试Ruby正则解析器,需优雅处理无效正则而非崩溃。补充说明:通过源码编译Ruby库,使用commit a8e7fee80129b0ba360c2671582117c8e18a6464;已知"*"是无效正则,但需程序正常处理。


崩溃原因

Ruby解析无效正则时会抛出异常,但当前代码未设置Ruby的异常保护机制。Ruby的C API通过longjmp实现异常抛出,若未提前设置tag保护,异常触发时会跳转到未初始化的上下文,直接引发段错误。

修复方案

使用Ruby C API的rb_protect函数包裹正则编译操作,捕获并处理异常,同时修正Ruby初始化顺序:

#include <ruby.h>
#include "ruby/re.h"

#define MAX_INPUT_SIZE 1000

// 包装rb_reg_regcomp,适配rb_protect的函数签名
static VALUE regcomp_wrapper(VALUE arg) {
    return rb_reg_regcomp(arg);
}

int main(int argc, char** argv) {
    VALUE x;
    char string[MAX_INPUT_SIZE];
    int setup_result;
    int exc_state; // 存储异常状态,0表示无异常

    // 修正Ruby初始化顺序:先ruby_init,再ruby_setup
    ruby_init();
    setup_result = ruby_setup();
    if (setup_result != 0) {
        return 1;
    }

    ruby_init_loadpath();

    memset(string, 0, MAX_INPUT_SIZE);

    fgets(string, MAX_INPUT_SIZE, stdin);

    if (string[MAX_INPUT_SIZE-2]) {
        ruby_cleanup(setup_result);
        return 0;
    }

    x = rb_str_new_cstr("*");
    // 使用rb_protect捕获正则编译时的异常
    VALUE re = rb_protect(regcomp_wrapper, x, &exc_state);

    if (exc_state != 0) {
        // 处理异常:获取并打印错误信息
        VALUE exc = rb_errinfo();
        VALUE err_msg = rb_obj_as_string(exc);
        fprintf(stderr, "正则编译失败:%s\n", RSTRING_PTR(err_msg));
        // 清除错误状态
        rb_set_errinfo(Qnil);
    } else {
        // 正则编译成功后的逻辑
        fprintf(stdout, "正则编译成功\n");
    }

    // 清理Ruby运行时环境
    ruby_cleanup(setup_result);
    return 0;
}

关键修改点

  • 修正初始化顺序:正确流程是先调用ruby_init初始化Ruby核心,再调用ruby_setup启动运行时,原代码顺序颠倒可能导致上下文初始化不完整。
  • 添加异常保护:rb_protect会捕获Ruby层面的异常,将状态存入exc_state,避免直接触发段错误。
  • 异常处理逻辑:通过rb_errinfo()获取异常对象,转换为字符串输出错误信息,最后清除错误状态保证程序继续运行。
  • 资源清理:退出前调用ruby_cleanup释放Ruby运行时资源,避免泄漏。

内容的提问来源于stack exchange,提问作者Some nerd who does not have a

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 15:30:05