execvp调用返回EFAULT(错误地址)errno,看似只有64位。谷歌搜索什么都没有发现

问题描述 投票:2回答:2

更新:使用valgrind运行git diff结果>]

 Syscall param execve(argv) points to uninitialised byte(s)

strace的输出未完全解码-即,字符串数组中的十六进制数字为argv。

...

开始时就像是超级用户问题,但现在肯定已经进入了SO的领域。

但是无论如何,这是我最初在SU上发表的帖子,在我非常仔细地查看源代码之前,先详细介绍了问题:https://superuser.com/questions/795751/various-methods-of-trying-to-set-up-a-git-diff-tool-lead-to-fatal-cannot-exec

本质上,遵循以下标准过程,通过在.gitconfig中的[diff]下设置外部指令,将vimdiff设置为diff工具会导致如下错误:

fatal: cannot exec 'git_diff_wrapper': Bad address
external diff died, stopping at HEAD:switch-monitor.sh.

发生在我的Linux Mint 17 64位操作系统以及virtualbox VM上的Ubuntu 14.04 64位OS上,但不是

在Ubuntu 14.04 32位 VM上...

Google搜索没有发现类似的问题。我花了很多时间查看git的源代码来弄清楚这一点。错误地址是strerror针对EFAULT错误返回的描述。这是来自execve联机帮助页的EFAULT的简短描述:

EFAULT filename points outside your accessible address space

我已经跟踪了git如何将错误消息拼凑在一起,并使用它来缩小问题来源的范围。让我们从这里开始:

static int execv_shell_cmd(const char **argv)
{
    const char **nargv = prepare_shell_cmd(argv);
    trace_argv_printf(nargv, "trace: exec:");
    sane_execvp(nargv[0], (char **)nargv);
    free(nargv);
    return -1;
}

此函数不应返回控制,但由于错误而可以。实际的execvp调用位于sane_execvp中,但也许prepare_shell_cmd很有趣,尽管我没有发现任何问题:

static const char **prepare_shell_cmd(const char **argv)
{
    int argc, nargc = 0;
    const char **nargv;

    for (argc = 0; argv[argc]; argc++)
        ; /* just counting */
    /* +1 for NULL, +3 for "sh -c" plus extra $0 */
    nargv = xmalloc(sizeof(*nargv) * (argc + 1 + 3));

    if (argc < 1)
        die("BUG: shell command is empty");

    if (strcspn(argv[0], "|&;<>()$`\\\"' \t\n*?[#~=%") != strlen(argv[0])) {
#ifndef GIT_WINDOWS_NATIVE
        nargv[nargc++] = SHELL_PATH;
#else
        nargv[nargc++] = "sh";
#endif
        nargv[nargc++] = "-c";

        if (argc < 2)
            nargv[nargc++] = argv[0];
        else {
            struct strbuf arg0 = STRBUF_INIT;
            strbuf_addf(&arg0, "%s \"$@\"", argv[0]);
            nargv[nargc++] = strbuf_detach(&arg0, NULL);
        }
    }

    for (argc = 0; argv[argc]; argc++)
        nargv[nargc++] = argv[argc];
    nargv[nargc] = NULL;

    return nargv;
}

看起来好像他们弄乱了终止的NULL指针(缺少它会导致EFAULT)。

sane_execvp非常简单。它是对execvp的调用,如果失败,则返回-1。

我还没有弄清楚trace_argv_printf的作用,尽管看起来它可能会影响nargv并可能破坏终止NULL指针?如果您希望我将其包含在这篇文章中,请告诉我。

到目前为止,我一直无法在自己的C代码中用execvp再现EFAULT。

这是git 1.9.1,源代码在这里:https://www.kernel.org/pub/software/scm/git/git-1.9.1.tar.gz

关于前进的任何想法?

谢谢

更新:使用valgrind运行git diff导致Syscall参数execve(argv)指向未初始化的字节,并且strace的输出未完全解码-即,...之间存在十六进制数字。

答案(从评论中复制):它似乎是git 1.9.1中的错误。在调用运行命令代码之前,(旧)diff.c代码大约在2910-2930行左右,用参数填充了大小为10的数组。但是在一种情况下,它会输入10个实际参数,然后输入11个参数NULL。取决于编译器的异想天开,NULL可能会被其他一些局部变量覆盖(或者NULL可能会覆盖重要的内容)。

将数组更改为大小11应该可以解决此问题。或者只是更新到较新的git(v2.0.0或更高版本); Jeff King在提交82fbf269b9994d172719b2d456db5ef8453b323dae049c955c8858899467f6c5c0259c48a5294385中用动态数组替换了硬编码数组。

注意:地址错误的另一个可能原因是run-command.c#exists_in_PATH()使用run-command.c#exists_in_PATH()。Git 2.26(2020年第一季度)已解决此问题。

请参阅run-command.c#sane_execvp()run-command.c#sane_execvp()(2020年1月7日)。[(由commit 63ab08f合并为brian m. carlson (bk2204),2020年1月22日)

bk2204:避免在Junio C Hamano -- gitster --中发生未定义的行为>

作者:Miriam R。

[签名人:brian m。卡尔森

在此函数中,我们释放从gitster获得的指针,然后检查它是否为commit 42096c7

但是,如果指针为非NULL,这是未定义的行为,因为C标准在释放它后不再允许我们使用有效的指针。

C标准将允许对此行为进行定义的唯一情况是,如果r为run-command,因为它声明在这种情况下,由于调用free而导致“不发生任何动作”。

很容易暗示这不太可能出现问题,但我们知道GCC确实利用了这样的事实,即即使与程序员的期望相反,也不会发生未定义的行为来优化和重写代码。

事实上,就像我们在这里一样,它经常忽略run-command指针检查。

因为它很容易修复,所以我们就这样做,并避免将来出现潜在的麻烦。

所以代替:

exists_in_PATH

您现在拥有:

locate_in_PATH
c linux git errno execvp
2个回答
2
投票

答案(从评论中复制):它似乎是git 1.9.1中的错误。在调用运行命令代码之前,(旧)diff.c代码大约在2910-2930行左右,用参数填充了大小为10的数组。但是在一种情况下,它会输入10个实际参数,然后输入11个参数NULL。取决于编译器的异想天开,NULL可能会被其他一些局部变量覆盖(或者NULL可能会覆盖重要的内容)。


0
投票

注意:地址错误的另一个可能原因是run-command.c#exists_in_PATH()使用run-command.c#exists_in_PATH()。Git 2.26(2020年第一季度)已解决此问题。

请参阅run-command.c#sane_execvp()run-command.c#sane_execvp()(2020年1月7日)。[(由commit 63ab08f合并为brian m. carlson (bk2204),2020年1月22日)

© www.soinside.com 2019 - 2024. All rights reserved.