答案(从评论中复制):它似乎是git 1.9.1中的错误。在调用运行命令代码之前,(旧)diff.c
代码大约在2910-2930行左右,用参数填充了大小为10的数组。但是在一种情况下,它会输入10个实际参数,然后输入11个参数NULL
。取决于编译器的异想天开,NULL可能会被其他一些局部变量覆盖(或者NULL可能会覆盖重要的内容)。
更新:使用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工具会导致如下错误:
在Ubuntu 14.04 32位 VM上...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上,但不是
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在提交82fbf269b9994d172719b2d456db5ef8453b323d
和ae049c955c8858899467f6c5c0259c48a5294385
中用动态数组替换了硬编码数组。
注意:地址错误的另一个可能原因是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
答案(从评论中复制):它似乎是git 1.9.1中的错误。在调用运行命令代码之前,(旧)diff.c
代码大约在2910-2930行左右,用参数填充了大小为10的数组。但是在一种情况下,它会输入10个实际参数,然后输入11个参数NULL
。取决于编译器的异想天开,NULL可能会被其他一些局部变量覆盖(或者NULL可能会覆盖重要的内容)。
注意:地址错误的另一个可能原因是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日)