【问题标题】:Segmentation Fault with Valgrind error "Invalid read of size 4"带有 Valgrind 错误的分段错误“大小为 4 的无效读取”
【发布时间】:2021-12-03 02:12:51
【问题描述】:

我正在尝试在 C 中从头开始实现 malloc,但是遇到了分段错误。当我通过 Valgrind 输入代码时,它说

First node at: 0x422c000
Size of free chunk: 8032
Next node at: 0x3e700000028
==350== Invalid read of size 4
==350==    at 0x1087A6: printFreeList (class_demo.c:29)
==350==    by 0x108840: test1 (class_demo.c:45)
==350==    by 0x108732: main (class_demo.c:16)
==350==  Address 0x3e700000030 is not stack'd, malloc'd or (recently) free'd

这是相关功能,如果需要我可以提供更多信息。该函数的一般工作方式是使用单链表来跟踪可用空间。每当我分配空间时,我都会为分配的块设置一个标头,指示块大小和一个幻数,然后返回分配空间的地址。

// allocate size bytes of memory
void *my_malloc(uint32_t size){  
    // error if size is negative
    // if (size < 0) {
    //     my_errno = MYENOMEM;
    //     return NULL;
    // }
    // make sure using 8 byte aligned size 
    size = actual_size(size);
    // calculate minimum chunk size
    int minimum_chunk_size = minimum_chonk_size(size);
    // if no freelistnode create one
    if (free_list_begin() == NULL) {
        head = (FreeListNode)sbrk(8192);
        head->flink = NULL;
        head->size = 8192;
    } 
    // initialize variables
    head = free_list_begin();
    struct freelistnode * prev = NULL;
    struct freelistnode * current = head;
    void* ptr;
    // find where to allocate chunk
    while (current != NULL) {
        // large enough 
        if (current->size >= minimum_chunk_size) { 
            // set return address
            ptr = current;
            if (current->size - minimum_chunk_size >= 16) { //  allocate tail end
                // set up new freelistnode
                struct freelistnode * new = (FreeListNode)(ptr + minimum_chunk_size);
                new->size = current->size - minimum_chunk_size;
                if (prev != NULL) {
                    prev->flink = new;
                }
                new->flink = current->flink;
            } else { 
                // remove current node
                if (prev != NULL) {
                    prev->flink = current->flink;
                } else {
                    head = current->flink;
                }  
            }
            break;
        // need more space
        } else if (current->flink == NULL) { 
            if (minimum_chunk_size <= 8192) {
                // set return address
                ptr = sbrk(8192);
                if (8192 - minimum_chunk_size >= 16) { // allocate tail end
                    // set up new freelistnode
                    struct freelistnode * new = (FreeListNode)(ptr + minimum_chunk_size);
                    new->size = 8192 - minimum_chunk_size;
                    if (prev != NULL) {
                        prev->flink = new;
                    }
                    new->flink = current->flink;
                } 
            } else {
                // set return address
                ptr = sbrk(minimum_chunk_size); 
            }
            break;
        // keep going through list
        } else {
            prev = current;
            current = current->flink;
        }
    }
    // set header
    *((uint32_t*)ptr) = minimum_chunk_size;
    *((int*)(ptr + 4)) = 999;
    // return the address
    return ptr + 8;
}

当我注释掉上面函数中的两行时:

// set header
*((uint32_t*)ptr) = minimum_chunk_size;
*((int*)(ptr + 4)) = 999;

分段错误消失了。但我看不出这两行是如何导致错误的。

这是调用函数的程序class_demo.c

#include "my_malloc.h"
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

void test1();

int main(int argc, const char * argv[])
{
    test1();

    return 0;
}

void printFreeList(){
    FreeListNode ptr = free_list_begin();
    printf("First node at: %p\n", ptr);
    printf("Size of free chunk: %d\n", ptr->size);
    ptr=ptr->flink;

    while(ptr != NULL){
        printf("Next node at: %p\n", ptr);
        printf("Size of free chunk: %d\n", ptr->size);
        ptr = ptr->flink;
    }
}

void test1(){
    char *a = my_malloc(32);
    printFreeList();
}

【问题讨论】:

  • if (size &lt; 0) { 应该给你一个警告.. sizeunsigned,所以它不可能是
  • 好的,谢谢,有道理
  • 不确定它是否相关,但你真的应该在每次调用它时检查sbrk 的返回值。这些调用中的任何一个都可能失败(返回(void *)-1。)
  • @NateEldredge 好的,谢谢,我现在看看
  • 您是否尝试按照我在回答中所说的去做?

标签: c segmentation-fault malloc valgrind


【解决方案1】:

void * ptr 中进行地址运算是未定义的。但这可能不是问题。

你需要看看为什么你的 printfs 中有两个非常不同的地址。 0x422c000 和 0x3e700000028。 0x3e700000028 值看起来很可疑。

Valgrind 在sbrk 这样低的级别上无法工作,因此如果您读取或写入sbrk 返回的内存,我预计它会产生错误。您应该按照here 的描述检测您的内存池。

或者,您可以改用malloc,而不是使用sbrk,仅用于测试。当您的代码使用 malloc 时,切换回 sbrk(但如果您希望 Valgrind 使用 sbrk 产生有意义的结果,您仍然需要检测)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-12
    • 1970-01-01
    • 2015-05-09
    • 1970-01-01
    • 2015-07-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多