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

自定义长算术函数触发AddressSanitizer堆缓冲区溢出问题求助

自定义长算术函数堆缓冲区溢出问题修复

问题描述

实现了一套自定义长算术函数,运行时触发AddressSanitizer堆缓冲区溢出错误,错误发生在inf_sum函数第72行,调用链路为inf_sum→inf_add→inf_product→inf_multi→inf_pow→main。仅通过__inf_domain_expansion函数管理__INTINF变量内存(接近溢出时扩容2倍),尝试调整扩容触发条件至使用率超80%仍未解决,确认是digits数组内存不足导致。

错误日志

==11157==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60e0004b72c0 at pc 0x55bf1bedcc01 bp 0x7ffc5d6d6c00 sp 0x7ffc5d6d6bf0
READ of size 8 at 0x60e0004b72c0 thread T0
    #0 0x55bf1bedcc00 in inf_sum /home/user/C/longArithmetic/intinf.c:72
    #1 0x55bf1bedcedc in inf_add /home/user/C/longArithmetic/intinf.c:91
    #2 0x55bf1bedd037 in inf_product /home/user/C/longArithmetic/intinf.c:106
    #3 0x55bf1bedd19e in inf_multi /home/user/C/longArithmetic/intinf.c:115
    #4 0x55bf1bedd2c5 in inf_pow /home/user/C/longArithmetic/intinf.c:127
    #5 0x55bf1bedc448 in main /home/user/C/longArithmetic/test.c:14
    #6 0x7f8425afed8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #7 0x7f8425afee3f in __libc_start_main_impl ../csu/libc-start.c:392
    #8 0x55bf1bedc264 in _start (/home/user/C/longArithmetic/test.out+0x1264)

0x60e0004b72c0 is located 0 bytes to the right of 160-byte region [0x60e0004b7220,0x60e0004b72c0)
allocated by thread T0 here:
    #0 0x7f8425e99887 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:145
    #1 0x55bf1bedc574 in inf /home/user/C/longArithmetic/intinf.c:16
    #2 0x55bf1bedd132 in inf_multi /home/user/C/longArithmetic/intinf.c:113
    #3 0x55bf1bedd2c5 in inf_pow /home/user/C/longArithmetic/intinf.c:127
    #4 0x55bf1bedc448 in main /home/user/C/longArithmetic/test.c:14
    #5 0x7f8425afed8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/user/C/longArithmetic/intinf.c:72 in inf_sum
Shadow bytes around the buggy address:
  0x0c1c8008ee00: fd fd fd fd fa fa fa fa fa fa fa fa fd fd fd fd
  0x0c1c8008ee10: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c1c8008ee20: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
  0x0c1c8008ee30: fd fd fd fd fd fd fd fd fd fd fd fd fa fa fa fa
  0x0c1c8008ee40: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c1c8008ee50: 00 00 00 00 00 00 00 00[fa]fa fa fa fa fa fa fa
  0x0c1c8008ee60: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c1c8008ee70: fd fd fd fd fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1c8008ee80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1c8008ee90: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1c8008eea0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==11157==ABORTING

关键定义

#define MODULO      (1000000000000000000ULL)
#define START_SIZE  (20)

typedef long long          int64;
typedef unsigned long long u64;
typedef unsigned char      u8;

原始函数源码

void inf_sum(__INTINF *dst, __INTINF *num1, __INTINF *num2)
{
    __inf_clear(dst);

    __INTINF *max_num = NULL;
    int64     max_count = 0;
    u8        carry_flag = 0;
    if (num1->count > num2->count) {
        max_num = num1;
        memset(num2->digits + num2->count, 0, num1->count - num2->count);
    }
    else if (num2->count > num1->count) {
        max_num = num2;
        memset(num1->digits + num1->count, 0, num2->count - num1->count);
    } else {
        max_num = num1;
    }
    max_count = max_num->count;

    for (int i = 0; i < max_count; ++i) {
        dst->count++;
        __inf_domain_expansion(dst);
        dst->digits[i] = num1->digits[i] + num2->digits[i] + carry_flag;
        carry_flag = 0;
        if (dst->digits[i] > MODULO - 1) {
            dst->digits[i] -= MODULO;
            carry_flag = 1;
        }
    }
    
    if (carry_flag) {
        dst->count++;
        __inf_domain_expansion(dst);
        dst->digits[dst->count] = carry_flag;
    }
}

void inf_add(__INTINF **num1, __INTINF *num2)
{
    __INTINF *new_num1 = inf();

    inf_sum(new_num1, *num1, num2);

    inf_free(*num1);

    *num1 = new_num1;
}

void inf_product(__INTINF **dst, __INTINF *num1, __INTINF *num2)
{
    __inf_clear(*dst);

    int64 times = 0;
    for (int i = 0; i < num2->count; ++i) {
        times = (num2->digits[i]) * (int64)pow(MODULO, i);;
        for (int j = 0; j < times; ++j) {
            inf_add(dst, num1);
        }
    }
}

void inf_multi(__INTINF **num1, __INTINF *num2)
{
    __INTINF *new_num1 = inf();

    inf_product(&new_num1, *num1, num2);

    inf_free(*num1);

    *num1 = new_num1;
}

void inf_pow(__INTINF **dst, __INTINF *num, u64 exp)
{
    __inf_copy(dst, num);

    for (int i = 0; i < exp - 1; ++i) {
        inf_multi(dst, num);
    }
}

static void __inf_domain_expansion(__INTINF *num)
{
    if (num->count >= num->size) {
        num->size *= 2;
        num->digits = (u64*)realloc(num->digits, num->size * sizeof(u64));
        memset(num->digits + num->count, 0, num->size);
    }
}

主程序代码

__INTINF *n1 = inf(); /* Allocate memmory to var n1 */
__INTINF *n2 = inf(); /* Allocate memmory to var n1 */

inf_put(n1, 127ULL); /* Initializes n1 data */

inf_pow(&n2, n1, 6512); /* Raises n1 to power of 6512 */

问题分析与修复方案

1. 核心错误点

(1) inf_sum中越界访问短数组的digits

当num1和num2长度不同时,直接用memset填充短数组的digits到长数组长度,但短数组的digits内存空间可能不足以容纳额外元素,导致越界读写。正确做法是不修改原数组,而是在求和时判断索引是否超出短数组的count,超出则取0。

(2) __inf_domain_expansion中memset长度错误

memset(num->digits + num->count, 0, num->size)的长度参数是字节数,但实际需要填充的是num->size - num->count个u64元素,正确长度应为(num->size - num->count) * sizeof(u64),否则会越界写内存。

(3) inf_sum进位处理时索引越界

dst->digits[dst->count] = carry_flag中,dst->count已经是加1后的元素个数,数组索引应从0开始,正确索引为dst->count - 1,否则会访问超出数组范围的内存。

(4) inf_product实现逻辑完全错误

用循环加法模拟乘法完全不可行(比如MODULO=1e18时,循环次数会达到天文数字),必须改用长乘法的逐位累加逻辑,否则程序无法正常运行且会导致内存异常。

2. 修复后的代码

void inf_sum(__INTINF *dst, __INTINF *num1, __INTINF *num2)
{
    __inf_clear(dst);

    int64 max_count = num1->count > num2->count ? num1->count : num2->count;
    u8    carry_flag = 0;

    for (int i = 0; i < max_count; ++i) {
        dst->count++;
        __inf_domain_expansion(dst);
        // 索引超出数组count时取0,避免越界访问
        u64 val1 = (i < num1->count) ? num1->digits[i] : 0;
        u64 val2 = (i < num2->count) ? num2->digits[i] : 0;
        dst->digits[i] = val1 + val2 + carry_flag;
        carry_flag = 0;
        if (dst->digits[i] >= MODULO) {
            dst->digits[i] -= MODULO;
            carry_flag = 1;
        }
    }
    
    if (carry_flag) {
        dst->count++;
        __inf_domain_expansion(dst);
        dst->digits[dst->count - 1] = carry_flag; // 修正索引
    }
}

void inf_add(__INTINF **num1, __INTINF *num2)
{
    __INTINF *new_num1 = inf();

    inf_sum(new_num1, *num1, num2);

    inf_free(*num1);

    *num1 = new_num1;
}

// 修复长乘法实现
void inf_product(__INTINF **dst, __INTINF *num1, __INTINF *num2)
{
    __inf_clear(*dst);
    // 乘法结果最多有num1->count + num2->count位
    int64 result_max_count = num1->count + num2->count;
    // 先扩容到足够大小
    while ((*dst)->size < result_max_count) {
        __inf_domain_expansion(*dst);
    }

    for (int i = 0; i < num1->count; ++i) {
        u8 carry = 0;
        for (int j = 0; j < num2->count; ++j) {
            int64 pos = i + j;
            // 计算当前位的累加值
            unsigned __int128 sum = (unsigned __int128)(*dst)->digits[pos] 
                                  + (unsigned __int128)num1->digits[i] * num2->digits[j]
                                  + carry;
            (*dst)->digits[pos] = (u64)(sum % MODULO);
            carry = (u8)(sum / MODULO);
        }
        // 处理当前i的进位
        int64 pos = i + num2->count;
        while (carry && pos < result_max_count) {
            unsigned __int128 sum = (unsigned __int128)(*dst)->digits[pos] + carry;
            (*dst)->digits[pos] = (u64)(sum % MODULO);
            carry = (u8)(sum / MODULO);
            pos++;
        }
    }

    // 更新实际有效位数
    (*dst)->count = result_max_count;
    while ((*dst)->count > 0 && (*dst)->digits[(*dst)->count - 1] == 0) {
        (*dst)->count--;
    }
    if ((*dst)->count == 0) {
        (*dst)->count = 1; // 至少保留一位0
    }
}

void inf_multi(__INTINF **num1, __INTINF *num2)
{
    __INTINF *new_num1 = inf();

    inf_product(&new_num1, *num1, num2);

    inf_free(*num1);

    *num1 = new_num1;
}

void inf_pow(__INTINF **dst, __INTINF *num, u64 exp)
{
    __inf_clear(*dst);
    // 初始化结果为1
    inf_put(*dst, 1ULL);

    __INTINF *base = inf();
    __inf_copy(&base, num);

    // 快速幂优化,避免重复乘法
    while (exp > 0) {
        if (exp % 2 == 1) {
            inf_multi(dst, base);
        }
        inf_multi(&base, base);
        exp /= 2;
    }
    inf_free(base);
}

static void __inf_domain_expansion(__INTINF *num)
{
    if (num->count >= num->size) {
        num->size *= 2;
        num->digits = (u64*)realloc(num->digits, num->size * sizeof(u64));
        // 修正memset长度:填充新增的元素,每个元素是u64
        memset(num->digits + num->count, 0, (num->size - num->count) * sizeof(u64));
    }
}

3. 额外优化

  • inf_pow改用快速幂算法,将时间复杂度从O(exp)降至O(log exp),避免6512
相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 12:52:54