调用 free() 时出现分段错误

Segmentation Fault in call to free()

我有一个不断抛出分段错误的测试用例。当我使用 gdb 试图找到段错误的位置时,我发现它在被测试代码中调用 free() 失败。

现在的问题是,传递给 free() 的参数是使用 malloc() 分配的,之后没有调整 (AFAICT)。

相关代码如下:

structure.h:

#pragma once

//===   Includes

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

#include "kv/backend.h"
#include "xm.h"
#include "constants.h"

//===   Defines

#define STRUCTURE_TYPE_NULL (0x00)  // invalid - we should never see this
#define STRUCTURE_TYPE_SUB  (0x01)  // substructure
#define STRUCTURE_TYPE_I64  (0x02)  // 64b signed integer
#define STRUCTURE_TYPE_U64  (0x03)  // 64b unsigned integer
#define STRUCTURE_TYPE_H64  (0x04)  // 64b translate to hex (ie: 0x'...')
#define STRUCTURE_TYPE_F64  (0x05)  // 64b IEEE 754 double float
#define STRUCTURE_TYPE_BLOB (0x06)  // variable length binary blob
#define STRUCTURE_TYPE_STRING   (0x07)  // variable length null terminated string
#define STRUCTURE_TYPE_TIME (0x08)  // 64b packed YMDhms time
#define STRUCTURE_TYPE_UNIXTIME (0x09)  // 64b signed unix time

//===   Structures

typedef struct {
    int64_t     year    :35;
    uint64_t    month   :4;
    uint64_t    day :5;
    uint64_t    hour    :5;
    uint64_t    minute  :6;
    uint64_t    second  :6;
} structure_time;

typedef struct structure_struct {
    uint8_t *key;
    union {
        int64_t         i64;
        uint64_t        u64;
        uint64_t        h64;
        double          f64;
        uint8_t         *blob;
        uint8_t         *string;
        structure_time      time;
        int64_t         unixtime;
        struct structure_struct *children;
    };
    union {
        uint16_t    len;    // blob + string (includes NULL terminator)
        uint16_t    count;  // children
    };
    uint8_t     type;
} structure;

//===   Special

static inline int cmp_structure (const void *arg1, const void *arg2) {
    const structure *a = arg1;
    const structure *b = arg2;
    return strcmp (a->key, b->key); // compare keys
}

#define SORT_NAME structure
#define SORT_TYPE structure
#define SORT_CMP(x, y)  cmp_structure (&x, &y)
#include "../deps/sort/sort.h"

//===   Functions

static inline void StructureSortChildren (structure *s) {
    structure_tim_sort (s->children, s->count);
    return;
}

int StructureAddChildren    (structure *parent, const structure *children, int count);
void StructureFree      (structure *s);

structure.c:

#include "structure.h"

int StructureAddChildren (structure *parent, const structure *children, int count) {
    if (parent->type != STRUCTURE_TYPE_SUB)
        return 1;   // yeah lets not cause a memory issue

    // realloc () may lose data
    structure *tmp = malloc ((parent->count + count) * sizeof (structure *));
    if (!tmp)
        return -1;  // memory failure

    // copy over the old children
    memcpy (tmp, parent->children, parent->count * sizeof (structure));
    if (parent->count > 0)
        free (parent->children);        // segfault occurs here
    parent->children = tmp;

    // copy over the new children
    memcpy (&parent->children[parent->count], children, count * sizeof (structure));
    parent->count += count;

    // resort the array to allow bsearch() to find members
    structure_tim_sort (parent->children, parent->count);

    return 0;
}

test_structure.c:

#include "test.h"
#include "../structure.h"

const char *key[4] = { "head", "number", "integer", "string" };
const char *text = "text";

void printstructure (const structure *s) {
    switch (s->type) {
        case STRUCTURE_TYPE_SUB: {
            printf ("Structure:\n"  \
                "\tType:\t%s\n" \
                "\tKey:\t%s\n"  \
                "\tnumber Count:\t%i\n\n",
                "(Sub)Structure", s->key, s->count);
            break;
        }

        case STRUCTURE_TYPE_STRING: {
//          assert (s->len == (strlen (s->string) + 1));    // NULL terminator
            printf ("Structure:\n"  \
                "\tType:\t%s\n" \
                "\tKey:\t%s\n"  \
                "\tValue:\t%s\n" \
                "\tLength:\t%i\n\n",
                "String", s->key, s->string, s->len);
            break;
        }

        case STRUCTURE_TYPE_I64: {
            printf ("Structure:\n"  \
                "\tType:\t%s\n" \
                "\tKey:\t%s\n"  \
                "\tValue:\t%i\n\n",
                "64-Bit Signed Integer", s->key, (int) s->i64);
            break;
        }

        case STRUCTURE_TYPE_F64: {
            printf ("Structure:\n"  \
                "\tType:\t%s\n" \
                "\tKey:\t%s\n"  \
                "\tValue:\t%f\n\n",
                "64-Bit FP Number", s->key, (float) s->f64);
            break;
        }
    }

    return;
}

void test (void) {
    structure *head, *number, *integer, *string;

    // basic allocations
    head = malloc (sizeof (structure));
    head->key = strdup (key[0]);
    head->type = STRUCTURE_TYPE_SUB;
    head->count = 0;

    number = malloc (sizeof (structure));
    number->key = strdup (key[1]);
    number->type = STRUCTURE_TYPE_F64;
    number->f64 = 3.1415;

    integer = malloc (sizeof (structure));
    integer->key = strdup (key[2]);
    integer->type = STRUCTURE_TYPE_I64;
    integer->i64 = -42;

    string = malloc (sizeof (structure));
    string->key = strdup (key[3]);
    string->type = STRUCTURE_TYPE_STRING;
    string->string = strdup (text);
    string->len = strlen (text) + 1;    // NULL terminator

    // lets see the current states
    printf ("\n---Structures Built---\n\n");
    printstructure (head);
    printstructure (number);
    printstructure (integer);
    printstructure (string);

    // lets put them together
    // head +>  number
    //  ->  integer
    //  ->  string
    StructureAddChildren (head, integer, 1);
    StructureAddChildren (head, string, 1);
    StructureAddChildren (head, number, 1);     // the SEGFAULT occurs on this call

    ...
}

int main (int argc, char *argv) {
    test ();
    return 0;
}

现在,SEGFAULT 发生在 StructureAddChildren() 的第三次调用中。具体来说,线上

StructureAddChildren (head, number, 1); // the SEGFAULT occurs on this call

test_structure.c开始,一旦调用StructureAddChildren,SEGFAULT就出现在

free (parent->children); // segfault occurs here

来自 structure.c.

我无法想象是什么导致了 SEGFAULT,因为 StructureAddChildren() 是唯一分配内存的点,没有其他东西会干扰该指针(写入它)。


那么,总的来说,是什么导致了 SEGFAULT,我该如何解决它?

StructureAddChildren函数中malloc的大小可能有误,请尝试用sizeof(structure)替换原来的sizeof(structure*)

// realloc () may lose data
structure *tmp = malloc ((parent->count + count) * sizeof (structure));//!!!
if (!tmp)
    return -1;  // memory failure

StructureAddChildren() 中的新(和旧)结构分配内存时,您使用 sizeof(structure *) 计算了新的大小,而它应该是 sizeof(structure)。结果,分配的 space 不足,后来写入超出了分配的 space。更正后的行应该是:

structure *tmp = malloc ((parent->count + count) * sizeof (structure));