c - 使用动态内存分配在函数返回时中止陷阱 6

标签 c buffer-overflow sigabrt dynamic-allocation

我在 OS X Mavericks 上使用 Apple LLVM 版本 5.1 (clang-503.0.40) 在 C 中调试“中止陷阱 6”错误时遇到问题。目标是构造一个数据结构word2class其中包含 num_words 词汇表中的每个单词words 该单词所属的类 ID 数组(任意,但例如:名词、动词、...,作为整数 ID)及其倒数class2word其中包含每个类 0 <= class < num_class它包含的单词数组。

为此,我维护了指向 long long 数组的全局指针数组。前向数据结构(word2class)和后向数据结构(class2words)的构造方式相同,暂且称之为A。 A 和每个元素 A[i] 指向的每个数组都是动态分配的。在构造时,我们知道 A 可能包含的元素的最大数量( num_wordsnum_class )。 A 是通过从文件中读取 index,id 对构造的(index 是一个词 ID,id 是一个类 ID)。每个id应该插入到 A[index] 指向的数组中.属于多个类别的单词存储为每个类别 ID 的单独行(例如 0<tab>0<newline>0<tab>1 )。在构造期间,我维护一个数组,每个数组的当前最大大小 A_max_size[i] , 以及每个数组中的当前元素数 A_cur_size[i] , 如果每个 *A[i] 变得太小则懒惰地 realloc() 。每当 A[i] 变得太小时,我都会通过因子 2 调整其大小来摊销内存分配成本。以下是代码的实际相关部分:

#include <stdio.h>
#include <stdlib.h>

// global arrays of pointers to long longs
long long **word2class = NULL, **class2word = NULL;
// bounds for each array pointed to by word2class[i] / class2word[j]
long long *w2c_cur_size = NULL, *c2w_cur_size = NULL;
// max number of words, passed as first argument argv[1]
// max number of classes, read from file
long long num_words, num_classes = 0;

void AllocateAndInsert(long long **A, long long *A_cur_size, long long *A_max_size,
    long long index, long long item) {
 if (A == NULL || A_cur_size == NULL || A_max_size == NULL) {
  printf("NULL pointer passed.\n"); exit(1);
 }
 // Check if A[index] is big enough, realloc if needed
 if (A_cur_size[index] + 1 >= A_max_size[index]) {
   if (A_max_size[index] == 0) A_max_size[index] = 1;
   else A_max_size[index] *= 2;  // amortize memory allocation
   // dynamically allocate a large enough array of long longs
   A[index] = (long long*)realloc(A[index], A_max_size[index] * sizeof(long long));
   if (A[index] == NULL) { 
    printf("(Re)allocation error.\n");
    exit(1);
   }
 }
 // insert item at end of array pointed to by A[index]
 A[index][A_cur_size[index]] = item;
 A_cur_size[index]++;
}

void PrintDS(long long **A, long long max_index, long long *A_size) {
 int i, j;
 for (i = 0; i < max_index; i++) {
  printf("Index %d:\n", i);
  for (int j = 0; j < A_size[i]; j++) {
   printf("%lld ", A[i][j]);
  }
  printf("\n");
 }
}

void BuildDataStruct(char *fname) {
 FILE *fin = fopen(fname, "r");
 long long index, id;
 long long *w2c_max_size, *c2w_max_size;     // temp variables used during construction

 if (fin == NULL) {
   printf("Error opening file.\n"); 
   exit(1);
 }

 // read max number of classes as first line from file
 if (fscanf(fin, "%lld\n", &num_classes) != 1) { 
   printf("File format error.\n"); 
   exit(1); 
 }
 // word2class
 w2c_max_size = calloc(num_words, sizeof(long long));  // initialized to 0s
 w2c_cur_size = calloc(num_words, sizeof(long long));
 word2class = calloc(num_words, sizeof(long long *));
 // class2word
 c2w_max_size = calloc(num_classes, sizeof(long long));
 c2w_cur_size = calloc(num_classes, sizeof(long long));
 class2word = calloc(num_classes, sizeof(long long *));
 if (w2c_max_size == NULL || w2c_cur_size == NULL || word2class == NULL ||
     c2w_max_size == NULL || c2w_cur_size == NULL || class2word == NULL) {
  printf("Allocation error.\n"); 
  exit(1);
 }

 while (!feof(fin)) {
   if (fscanf(fin, "%lld\t%lld\n", &index, &id) != 2) {
     printf("Format error.\n");
     exit(1);
   }
   if (index < 0 || index >= num_words || id < 0 || id >= num_classes) { 
    printf("Bounds error.\n"); 
    exit(1); 
   }
   AllocateAndInsert(word2class, w2c_cur_size, w2c_max_size, index, id);
   AllocateAndInsert(class2word, c2w_cur_size, c2w_max_size, id, index);
 }

 fclose(fin);
 free(w2c_max_size);
 free(c2w_max_size);
 // DATA STRUCTURE PRINTS FINE HERE
 PrintDS(word2class, num_words, w2c_cur_size);
 PrintDS(class2word, num_classes, c2w_cur_size);
}

int main(int argc, char *argv[]) {
  if (argc <= 2) { 
    printf("Usage: %s num_words word2class_file\n", argv[0]); 
    exit(1); 
  }
  num_words = atoi(argv[1]);
  BuildDataStruct(argv[2]);
  // <-- 'Abort Trap 6' RAISED HERE
  return 0;
}

对于那些感兴趣的人,可以使用以下脚本生成测试数据 python script.py num_words max_class_id max_class_per_word out_file并使用上述 C 代码作为 ./build_ds num_words out_file 使用:

import sys
from random import sample, randint

num_words = int(sys.argv[1])
max_class_id = int(sys.argv[2])
max_class_per_word = int(sys.argv[3])
f = open(sys.argv[4], 'w')

f.write("%d\n" % (max_class_id,))
for w in xrange(num_words):
    num_classes = randint(1, max_class_per_word)
    classes = sample(xrange(max_class_id), num_classes)
    for c in classes:
        f.write("%d\t%d\n" % (w, c))

f.close()

此代码有效(即使在“真实”数据上),但是,一旦我将此确切代码用作更大应用程序的一部分,程序就会终止并出现神秘的“Abort trap 6”错误。 BuildDataStruct()从另一个函数调用,似乎对构造部分工作正常。从 BuildDataStruct() 返回之前可以打印出这两种数据结构没问题。但是,在函数返回后立即生成错误。

通过阅读其他各种答案,Abort Trap: 6当缓冲区被覆盖时,OS X 上似乎会出现错误。但是,我添加了对 index 的检查确保不会发生这种情况,所以我不确定还有什么问题?我可能会以某种方式覆盖堆栈吗?有没有人知道我可以在哪里或如何开始寻找问题?

最佳答案

您的代码存在一些问题:

  1. 它没有编译,因为几个 { 被注释掉了:

    if (A == NULL) { // handle allocation error }
    

    我假设这只是转录错误。

  2. 您不检查 fscanf 的返回值确保读取了两个参数。文件中的任何语法错误都可能导致损坏。

  3. 您从每一行读取两个字段,indexid,但只使用第一个。第二个被忽略了。此外,您永远不会在 cur_size 数组中设置任何值,因此它在您的代码中始终为零。

    所以,我要作一个假设:index是外层数组A中的索引,id是一个索引到相应的内部数组中,即您希望能够分配 A[index][id] 存在的足够大的数组。

  4. 根据我的假设,存在一个算法问题:如果max_size[index] 小于cur_size[index],则将其加倍——但是不要认为 cur_size[index] 可能是原来的两倍多。您需要将大小加倍确保它大于id+1

  5. 您应该保留 cur_size 数组,以便稍后检查越界索引。

  6. calloc 用零填充内存,但是 realloc使新分配部分的值不确定。

  7. 在我的平台上,long long 是 64 位,但 callocrealloc 接受 size_t第一个参数,所以我看到了几个“从‘__int64’到‘size_t’的转换,可能丢失数据”错误。您应该检查以确保 calloc 在您的平台上使用 long long

因此,问题 1-6 的修复可能如下所示:

long long **A = NULL; // global array of pointers to long longs
long long *A_cur_size_array = NULL;

long long N = 1000; // for exax1mple

// If your platform has a predefined version of "max", use it instead.
static long long max_long_long(long long a, long long b)
{
    // If your platform has a predefined version of "max", use it instead.
    return (a > b) ? a : b;
}

void construct_A(char *fname) 
{
    FILE *fin = fopen(fname, "r");
    long long index, id;
    long long *max_size_array;

    A_cur_size_array = calloc(N, sizeof(int));
    A = calloc(N, sizeof(long long *));
    max_size_array = calloc(N, sizeof(int));   // init to 0

    if (A == NULL || max_size_array == NULL || A_cur_size_array == NULL) 
    { 
        // handle allocation error 
    }
    while (!feof(fin)) 
    {
        if (fscanf(fin, "%lld\t%lld\n", &index, &id) != 2)
        {
            printf("File format error.\n"); 
            exit(1); 
        }
        if (index < 0 || index >= N) 
        { 
            printf("BOUNDS ERROR\n"); 
            exit(1); 
        }
        if (id < 0)
        {
            printf("BOUNDS ERROR\n"); 
            exit(1); 
        }
        if (id >= A_cur_size_array[index])
        {
            A_cur_size_array[index] = id + 1;
            if (A_cur_size_array[index] >= max_size_array[index]) 
            {
                long long new_max_size = max_long_long(id + 1, 2 * max_size_array[index]);
                long long i;
                // dynamically allocate a large enough array of long longs
                A[index] = (long long*)realloc(A[index], new_max_size * sizeof(long long));
                if (A[index] == NULL) 
                { 
                    // handle allocation error 
                }
                for (i = max_size_array[index]; i < new_max_size; i++)
                    A[index][i] = 0;
                max_size_array[index] = new_max_size;
            }
        }
    }
    fclose(fin);
    free(max_size_array);
}

关于c - 使用动态内存分配在函数返回时中止陷阱 6,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26514952/

相关文章:

数组中的 char 指针值

c - 使用 goto 跳过 if 语句是否更快

嵌入式系统上的 C++ 动态代码注入(inject)

c++ - 缓冲区溢出示例:为什么此代码很危险?

Swift 命令行工具运行时崩溃 dyld : Symbol not found: __pthread_atfork_child

c - 如何使用与其他 .o 文件一起编译的 .o 文件进行编译 (C99)

c - 如何从带有反馈的缓冲区中读取,缓冲区不会溢出?

c - 阿姆斯壮数字缓冲区溢出

ios - 应用程序在iPhone 5上运行但在iPhone 4s上未运行,但出现错误SIGABRT

swift - 如何调试信号 SIGABRT