NULL pointer dereference in `git_worktree_lookup` on a repository without a git directory

未关闭 适合新手
#7,462 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

还没有人认领这个 Issue。

评估

难度
2/5
预计耗时
1-3 小时
新手友好度
78/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
c, git

调研方向

阅读 src/libgit2/worktree.c 中的 git_worktree_lookup,并比较 git_worktree_list() 如何处理没有公共目录的仓库。首先检查现有的 worktree 测试,查找用于设置仓库的辅助函数,然后添加一个仅使用对象数据库的回归测试用例。完成标准是查找返回 GIT_ENOTFOUND 且不会崩溃。

由索引模型根据 Issue 内容生成。

描述


First of all, thank you so much for maintaining libgit2; while applying our testing tool across multiple Rust crates during its evaluation, we encountered an issue in libgit2 (reached through the Rust binding git2) that I would like to bring to your attention.

git_worktree_lookup() passes a NULL repo->commondir to strlen when the repository has no git directory, for example one created with git_repository_wrap_odb().

I notice the following code may have a problem: https://github.com/libgit2/libgit2/blob/42a89d7cc9374a19e72fa57011e5fe3a99c7b3bf/src/libgit2/worktree.c#L189

Describe the bug

int git_worktree_lookup(git_worktree **out, git_repository *repo, const char *name)
{
    ...
    GIT_ASSERT_ARG(repo);
    GIT_ASSERT_ARG(name);

    *out = NULL;

    if ((error = git_str_join3(&path, '/', repo->commondir, "worktrees", name)) < 0)
        goto out;

A repository made by git_repository_wrap_odb() has no git directory, so repo->commondir is NULL. git_str_join3() calls strlen() on each of its three strings (src/util/str.c#L816) and reads from address 0.

git_worktree_list() uses git_str_joinpath() on the same field instead, which accepts NULL, so it does not crash in this case.

Tested on libgit2 1.9.7, the latest release (as bundled by libgit2-sys 0.18.8+1.9.7, used by git2 0.21.0). Current main (42a89d7) has the same code.

To Reproduce

  1. Create a repository without a git directory with git_odb_new + git_repository_wrap_odb.
  2. Call git_worktree_lookup(&wt, repo, "a").

Through the safe Rust binding git2 0.21.0:

fn main() {
    // a repository with an object database only, no git directory
    let repo = git2::Repository::from_odb(git2::Odb::new().unwrap()).unwrap();

    let _ = repo.find_worktree("a");
}

Expected behavior

The lookup should return "not found" (GIT_ENOTFOUND), since such a repository has no worktrees, not crash the process.

output (AddressSanitizer, libgit2 built with -fsanitize=address):

==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000
==The signal is caused by a READ memory access.
    #1 strlen
    #2 git_str_join3
    #3 git_worktree_lookup
    #4 git2::repo::Repository::find_worktree

Without AddressSanitizer the process is killed by SIGSEGV.

Suggested fix

Return "not found" when there is no common directory (src/libgit2/worktree.c, git_worktree_lookup):

*out = NULL;

/* a repository without a git directory (git_repository_wrap_odb) has no worktrees */
if (repo->commondir == NULL) {
    git_error_set(GIT_ERROR_WORKTREE, "worktree '%s' not found", name);
    return GIT_ENOTFOUND;
}

if ((error = git_str_join3(&path, '/', repo->commondir, "worktrees", name)) < 0)
    goto out;

I applied this to the libgit2 1.9.7 sources bundled in libgit2-sys. The reproducer then gets GIT_ENOTFOUND (worktree 'a' not found) with no crash.

Thank you for your time.

主要语言
C
星标
10.6k
派生
2.7k
平均合并
1 天 5 小时
30 天内合并 PR
5

环境准备

在 Codespaces 中打开

在浏览器里用你自己的 GitHub 账号启动这个项目的开发容器。

  • 没有 Dockerfile 或 Docker Compose 文件
  • 没有 Pull Request 模板
  • 阅读贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

libgit2/libgit2 的其他 Issue

查看 libgit2/libgit2 的全部 Issue

相似的 Issue

更多 C Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。