strncpy()导致segfault

6jygbczu  于 2023-01-01  发布在  其他
关注(0)|答案(1)|浏览(134)

Stack Overflow上有很多类似的帖子,我仔细地查看了这些帖子,并将它们链接到本文的末尾。这些现有的帖子没有什么帮助,因为它们演示了将内存的未初始化部分传递给strncpy()函数的char *dest参数所导致的segfault。这些帖子中的另一个共同主题是强烈建议不要使用strncpy()。但是我读过的唯一的替代建议是使用strlcpy()代替。
我的程序使用一个静态分配的FS_Info结构体数组来存储文件系统条目的信息,其思想是输出指定目录中最大的10个文件的名称和大小,由于数组的大小是固定的,当发现文件系统条目大于数组中最小的条目时,我的程序尝试用描述新的更大条目的值来更新描述这个更小条目的结构。

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>

// size of array used to contain filesystem entries
const size_t fs_info_arr_size = 10;

// A struct to contain the name of a filesystem entry and its size in bytes.
typedef struct FS_Info
{
    char name[PATH_MAX];
    long long size;
} FS_Info;

// global pointer to FS_Info array
FS_Info *fs_info_arr[fs_info_arr_size];

// used to sort fs_entries array descending in terms of entry size
static int compare(const void *a, const void *b)
{
    const struct FS_Info *entryA = (FS_Info *)a;
    const struct FS_Info *entryB = (FS_Info *)b;

    return (entryB->size - entryA->size) - (entryA->size - entryB->size);
}

/*
Iterates over an array of FS_Info structs and returns a pointer to the struct having the smallest size member
*/
FS_Info *get_smallest_entry(FS_Info **entries)
{
    long long smallest = entries[0]->size;
    FS_Info *target;

    for (int i = 1; i < fs_info_arr_size * sizeof(FS_Info); i += sizeof(FS_Info))
    {
        if (entries[i]->size < smallest)
        {
            smallest = entries[i]->size;
            target = entries[i];
        }
    }
    return target;
}

/*
Add entires to the array. If the array is full, use the above function to find the
struct having the smallest file size, and if the current file size is larger, replace it.
*/
void update_fs_info_arr(char *path) // FIXME debug call stack shows that using strncpy here causes segfault
{
    static int items_added = 0;

    struct stat st;
    if (stat(path, &st) == 0)
    {
        if (items_added < fs_info_arr_size) // if array capacity will not be exceeded
        {
            strncpy(fs_info_arr[items_added]->name, path, PATH_MAX);
            // strlcpy(fs_info_arr[items_added]->name, path, sizeof(fs_info_arr[items_added]->name));
            // strncpy(fs_info_arr[items_added]->name, path, sizeof(fs_info_arr[items_added]->name) / sizeof(fs_info_arr[items_added]->name[0]) - 1);
            // fs_info_arr[items_added]->name[sizeof(fs_info_arr[items_added]->name) / sizeof(fs_info_arr[items_added]->name[0]) - 1] = 0;
            fs_info_arr[items_added]->size = st.st_size;

            items_added++;
        }
        else
        // find entry having the smallest size and replace it with the current entry if it is larger
        {
            FS_Info *smallest = get_smallest_entry(fs_info_arr);
            if (st.st_size > smallest->size)
            {
                strncpy(smallest->name, path, PATH_MAX);
                // strlcpy(smallest->name, path, sizeof(smallest->name));
                // strncpy(smallest->name, path, sizeof(smallest->name) / sizeof(smallest->name[0]) - 1);
                // smallest->name[sizeof(smallest->name) / sizeof(smallest->name[0]) - 1] = 0;
                smallest->size = st.st_size;
            }
        }
    }
    else
    {
        printf("Error getting stat for entry %s: %d\n", path, stat(path, &st));
    }
}

void walk(const char *currDir)
{
    DIR *dir = opendir(currDir);
    struct dirent *entry;

    if (dir == NULL)
    {
        printf("%s could not be opened", currDir);
        return;
    }

    while ((entry = readdir(dir)) != NULL)
    {
        // if directory is current dir or parent dir
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
        {
            continue;
        }

        char path_to_entry[PATH_MAX];
        snprintf(path_to_entry, sizeof(path_to_entry), "%s/%s", currDir, entry->d_name);
        //snprintf(path_to_entry, sizeof(path_to_entry) - 1, "%s/%s", currDir, entry->d_name);
        //path_to_entry[sizeof(path_to_entry) - 1] = '\0';

        update_fs_info_arr(path_to_entry);

        if (entry->d_type == DT_DIR)
        {
            walk(path_to_entry);
        }
    }
    closedir(dir);
}

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

    char target_dir[PATH_MAX];

    strncpy(target_dir, argv[1], PATH_MAX);

    printf("Finding the %zu largest files in: %s\n", fs_info_arr_size, target_dir);

    // recursively visit all entries in the specified directory
    walk(target_dir);

    // sort the entries descending by file size
    qsort(fs_info_arr, fs_info_arr_size, sizeof(*fs_info_arr), compare);

    // output ten largest files found
    for (int i = 0; i < fs_info_arr_size; i++)
    {
        printf("%s\t%lld\n", fs_info_arr[i]->name, fs_info_arr[i]->size);
    }

    return EXIT_SUCCESS;
}

下面链接的帖子无法初始化strncpy()复制到的内存,或者包含错误的size_t num参数(如指定的here)。在后面的情况下,我尝试更改代码以匹配下面第一个链接的帖子中描述的模式,但没有效果。
strncpy leading to segmentation fault
Segmentation fault when calling strcpy
Segmentation fault: 11 (caused by strncpy())
Why do I get a segmentation fault when using strncpy?
strncpy segfault
Segfault on strncpy call
Segmentation Fault when using strncpy in c

编辑:

我已经按照答案部分的建议进行了修复,并意识到为什么我的程序逻辑有缺陷,但这个程序仍然导致错误访问:

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>

// size of array used to contain filesystem entries
const size_t fs_info_arr_size = 10;

/*
    A struct to contain the name of a filesystem entry and its size in bytes.
    An array of this type will be used to catalog all filesystem entries for
    the directory specified as command line argument.
*/
typedef struct FS_Info
{
    char name[PATH_MAX];
    long long size;
} FS_Info;

// global pointer to FS_Info array
FS_Info fs_info_arr[fs_info_arr_size];

// used to sort fs_entries array descending in terms of entry size
static int compare(const void *a, const void *b)
{
    const struct FS_Info *entryA = (FS_Info *)a;
    const struct FS_Info *entryB = (FS_Info *)b;

return entryB->size - entryA->size;
}

/*
Iterates over an array of FS_Info structs and returns a pointer to the struct
having the smallest size member.
*/
FS_Info *get_smallest_entry(FS_Info *entries)
{
    long long smallest = entries[0].size;
    FS_Info *target;

    for (int i = 1; i < fs_info_arr_size; i++)
    {
        if (entries[i].size < smallest)
        {
            smallest = entries[i].size;
            target = &entries[i];
        }
    }
    return target;
}

/*
Add entires to the array. If the array is full, use the above function to find the
struct having the smallest file size, and if the current file size is larger, replace it.
*/
void update_fs_info_arr(char *path) // FIXME debug call stack shows that using strncpy here causes segfault
{
    static int items_added = 0;

    struct stat st;
    if (stat(path, &st) == 0)
    {
        if (items_added < fs_info_arr_size) // if array capacity will not be exceeded
        {
            strncpy(fs_info_arr[items_added].name, path, PATH_MAX);
            fs_info_arr[items_added].size = st.st_size;

            items_added++;
        }
        else
        // find entry having the smallest size and replace it with the current entry if it is larger
        {
            FS_Info *smallest = get_smallest_entry(fs_info_arr);
            if (st.st_size > smallest->size)
            {
                strncpy(smallest->name, path, PATH_MAX);
                smallest->size = st.st_size;
            }
        }
    }
    else
    {
        printf("Error getting stat for entry %s: %d\n", path, stat(path, &st));
    }
}

void walk(const char *currDir)
{
    DIR *dir = opendir(currDir);
    struct dirent *entry;

    if (dir == NULL)
    {
        printf("%s could not be opened", currDir);
        return;
    }

    while ((entry = readdir(dir)) != NULL)
    {
        // if directory is current dir or parent dir
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
        {
            continue;
        }

        char path_to_entry[PATH_MAX];
        snprintf(path_to_entry, sizeof(path_to_entry), "%s/%s", currDir, entry->d_name);

        update_fs_info_arr(path_to_entry);

        if (entry->d_type == DT_DIR)
        {
            walk(path_to_entry);
        }
    }
    closedir(dir);
}

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

    // a char array to hold a filesystem path
    char target_dir[PATH_MAX];

    strncpy(target_dir, argv[1], PATH_MAX);

    printf("Finding the %zu largest files in: %s\n", fs_info_arr_size, target_dir);

    // recursively visit all entries in the specified directory
    walk(target_dir);

    // sort the entries descending by file size
    qsort(fs_info_arr, fs_info_arr_size, sizeof(*fs_info_arr), compare);

    // output ten largest files found
    for (int i = 0; i < fs_info_arr_size; i++)
    {
        printf("%s\t%lld\n", fs_info_arr[i].name, fs_info_arr[i].size);
    }

    return EXIT_SUCCESS;
}
bprjcwpo

bprjcwpo1#

OP:"* 我的程序使用静态分配的FS_Info结构体数组来存储有关文件系统项的信息。*"
代码:

FS_Info *fs_info_arr[fs_info_arr_size];

您正在分配指针,但从未分配空间来存储指针可能指向的内容...
试试看

FS_Info fs_info_arr[fs_info_arr_size];

并对代码进行正确的修改,以使用结构体数组(而不是全为NULL的指针数组)。
还有...

static int compare(const void *a, const void *b)
{
    const struct FS_Info *entryA = (FS_Info *)a;
    const struct FS_Info *entryB = (FS_Info *)b;

    return (entryB->size - entryA->size) - (entryA->size - entryB->size);
}

是不必要的复杂。降序只需使用

return entryB->size - entryA->size;

(and你可以向strncpy()道歉,因为它的声誉受到了不恰当的诋毁。)

相关问题