【问题标题】:fork with invalid command cause a memory leak in valgrind带有无效命令的 fork 导致 valgrind 中的内存泄漏
【发布时间】:2017-04-28 20:33:43
【问题描述】:

我有以下代码在分叉中执行无效命令。 以下代码在 valgrind 中返回内存泄漏。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>


int external_cmd(char **argv)
{
    int pid;

    if ((pid = fork()) == -1)
        return -1;

    if (pid == 0) {
        /* child */
        execvp(argv[0], argv);
        exit(0);

    } else if (pid < 0)
        return -1;

    int status;
    while (wait(&status) != pid);

    return 0;
}

int main ()
{
    char *argv[8] = {0};
    argv[0] = "tawtaw"; //<--------- invalid command
    argv[1] = "-a";

    char *mem = strdup("anychar");

    /* fork call */
    external_cmd(argv);

    free(mem);

   return(0);
}

使用 valgrind return 执行上述代码:

$ valgrind --leak-check=full --show-leak-kinds=all ./test
==11573== Memcheck, a memory error detector
==11573== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==11573== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==11573== Command: ./test
==11573== 
==11574== 
==11574== HEAP SUMMARY:
==11574==     in use at exit: 8 bytes in 1 blocks
==11574==   total heap usage: 1 allocs, 0 frees, 8 bytes allocated
==11574== 
==11574== 8 bytes in 1 blocks are still reachable in loss record 1 of 1
==11574==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11574==    by 0x4EBF729: strdup (strdup.c:42)
==11574==    by 0x400747: main (in /home/mohamed/Desktop/tech/test/test)
==11574== 
==11574== LEAK SUMMARY:
==11574==    definitely lost: 0 bytes in 0 blocks
==11574==    indirectly lost: 0 bytes in 0 blocks
==11574==      possibly lost: 0 bytes in 0 blocks
==11574==    still reachable: 8 bytes in 1 blocks
==11574==         suppressed: 0 bytes in 0 blocks
==11574== 
==11574== For counts of detected and suppressed errors, rerun with: -v
==11574== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==11573== 
==11573== HEAP SUMMARY:
==11573==     in use at exit: 0 bytes in 0 blocks
==11573==   total heap usage: 1 allocs, 1 frees, 8 bytes allocated
==11573== 
==11573== All heap blocks were freed -- no leaks are possible
==11573== 
==11573== For counts of detected and suppressed errors, rerun with: -v
==11573== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

注意:如果我使用有效命令“ls”而不是“tawtaw”执行代码,则 valgring 将不会返回内存泄漏。

我错过了什么?

【问题讨论】:

  • execvp() 失败后终止子进程有什么问题,就像您对exit(0) 所做的那样?看起来它实际上比修改堆然后终止要快一点。无论哪种方式,操作系统都会释放所有进程的资源。这不是您想要继续进行的泄漏,而是存在内存问题。而且它不像您在使用共享内存,因此您不会影响其他进程或浪费共享资源。

标签: c linux memory-leaks valgrind


【解决方案1】:

这是意料之中的。当execve() 无法执行命令时,它会将控制权返回给您的代码,然后您退出并且永远不会从strdup() 释放内存。

execve 成功时,整个文件映像被替换,strdup() 分配的内存将一无所有。

【讨论】:

  • 是的,它在父进程中释放(11573),但在子进程中没有释放(11574)。
  • @MOHAMED,但是当你分叉时,所有内存都在子进程中复制,包括分配给strdup() 的内存 - 而你没有在子进程中释放。这正是 valgrind 在这里报告的内容。
  • 我没有发现任何问题。
  • 但这是意料之中的。
  • 没有exec* 退出。如前所述,它们将进程正在运行的程序替换为另一个。
【解决方案2】:

你不应该关心这个“泄漏”,因为它只是你的进程异常终止。当您只需要打印一条错误消息并退出时,您不需要小心释放资源。操作系统将释放所有未完成的资源。

如果您出于某种原因仍然对此感到担忧,请尝试将 exit(0) 替换为 return -1;。在实际程序中,您永远不会使用exit,而只会使用释放分配资源的正常返回路径。在 C++ 中,使用 RAII 并在需要异常退出时抛出异常。这是您转换为 RAII 样式的程序:

#include <cstdlib>
#include <cstdarg>
#include <cerrno>
#include <vector>
#include <string>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <stdexcept>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

// This function should be standard in some kind of POSIX C++ library
void execvp(const std::string& program, const std::vector<std::string>& args)
{
    std::vector<const char*> real_argv(args.size()+1);
    for (const std::string& s : args)
        real_argv.push_back(s.c_str());
    real_argv.push_back(nullptr);
    // have to use const_cast because of the broken const model of C
    execvp(program.c_str(), const_cast<char**>(real_argv.data()));
    throw std::runtime_error((std::string("Could not execvp ") + args[0]).c_str());
}

int external_cmd(const std::string& program, const std::vector<std::string>& args)
{
    int pid;

    if ((pid = fork()) == -1)
        return -1;

    if (pid == 0) {
        /* child */
        execvp(program, args);

    } else if (pid < 0)
        return -1;

    int status;
    while (wait(&status) != pid);

    return 0;
}

int main ()
{
    try {
        std::vector<std::string> args;
        args.push_back("tawtaw");
        args.push_back("-a");

        std::string s("12345678"); // RAII

        /* fork call */
        external_cmd(args[0], args);

        return EXIT_SUCCESS;
    } catch (std::exception& e) {
        std::cerr << e.what() << ". Exiting.\n";
        return EXIT_FAILURE;
    } catch ( ... ) {
        std::cerr << "Unexpected error. OS message is: " << strerror(errno) << ". Exiting.\n";
        return EXIT_FAILURE;
    }
}

【讨论】:

  • 我在寻找 c 的解决方案。可能我必须删除 c++ 标签
  • @MOHAMED 下次请使用适当的语言标签。 C 没有预先打包的“解决方案”。您要么自己释放内存,要么忽略“泄漏”。
  • return -1 解决了这个问题,但是分叉​​需要时间来完成
猜你喜欢
  • 2021-03-09
  • 2021-07-23
  • 2021-04-20
  • 2018-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-24
  • 2022-08-14
相关资源
最近更新 更多