Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

[BUG] Uninitialized `db` freed in `git_refdb_open` on allocation failure

オープン 初心者向け
#7,421 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 12 日以内に返信

まだ誰も着手していません。

評価

難易度
2/5
見積もり時間
1〜3時間
初心者へのやさしさ
72/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
c, git
領域
backend

調査の方向性

src/libgit2/refdb.c の git_refdb_open から始めてください。ここではローカル変数 db が初期化子なしで宣言されており、git_refdb_new が失敗した後のエラーパスで解放されています。添付の 06.c アロケーション失敗再現コードを libgit2 に対してビルドし、スキャンと併せて実行して N = 2 でのセグメンテーション違反を確認してください。db を NULL で初期化し、スキャンがクラッシュせず適切なエラーで完了すれば完了です。

索引モデルが issue の本文から書いたものです。

説明

libgit2 commit 07b89d13e src/libgit2/refdb.c:42-74. Introduced by 806a0062f ("repository: wire up refStorage extension"); not in any release.

Bug

db is never initialized, and git_refdb_new does not write *out when its allocation fails. The error path then frees the garbage pointer, so the process gets SIGSEGV where it should get -1:

git_refdb *db;                                                /* 43: uninitialized */
...
if ((error = git_refdb_new(&db, repo)) < 0)                   /* db left unset on OOM */
    goto out;
...
out:
    if (error)
        git_refdb_free(db);                                   /* 74: frees garbage */

The fix is git_refdb *db = NULL; (git_refdb_free ignores NULL).

Reproduce

06.c installs a counting allocator through GIT_OPT_SET_ALLOCATOR that returns NULL for the Nth allocation, which looks exactly like a real OOM to libgit2. It creates a one-commit repo, opens it, and runs git_reference_lookup("HEAD"), which opens the refdb, with the Nth allocation failing, for N = 1..200. Only allocations made during the lookup can fail. The crashing N depends on the build, which is why the program scans instead of hard-coding it.

gcc -g -O0 06.c -I"$LIBGIT2_INCLUDE" "$LIBGIT2_LIBDIR/libgit2.a" -lz -lpthread -ldl -o 06_c
./06_c                                    # scan
gdb -ex run -ex bt --args ./06_c <N>      # single index
  alloc #0: OK
  alloc #1: clean error (handled)
Segmentation fault                        # N = 2, exit 139

#0 git_atomic32_dec               src/util/thread.h:136
#1 git_refdb_free                 src/libgit2/refdb.c:126
#2 git_refdb_open                 src/libgit2/refdb.c:74
#3 git_repository_refdb__weakptr  src/libgit2/repository.c:1656
#4 git_reference_lookup_resolved  src/libgit2/refs.c:252
06.c
#include <git2.h>
#include <git2/sys/alloc.h>
#include <stdio.h>
#include <stdlib.h>

static long fail_at, count; /* fail the fail_at-th allocation; 0 = never */

static int should_fail(void) { return fail_at && ++count == fail_at; }

static void *x_malloc(size_t n, const char *file, int line)
{
	(void)file; (void)line;
	return should_fail() ? NULL : malloc(n ? n : 1);
}

static void *x_realloc(void *p, size_t n, const char *file, int line)
{
	(void)file; (void)line;
	return should_fail() ? NULL : realloc(p, n ? n : 1);
}

/* A repo with one (empty-tree) commit. */
static int make_repo(const char *dir)
{
	git_repository *repo;
	git_signature *sig = NULL;
	git_index *idx = NULL;
	git_tree *tree = NULL;
	git_oid id;
	int rc;

	if ((rc = git_repository_init(&repo, dir, 0)) < 0)
		return rc;
	if ((rc = git_signature_new(&sig, "r", "[email protected]", 1234567890, 0)) == 0 &&
	    (rc = git_repository_index(&idx, repo)) == 0 &&
	    (rc = git_index_write_tree(&id, idx)) == 0 &&
	    (rc = git_tree_lookup(&tree, repo, &id)) == 0)
		rc = git_commit_create_v(&id, repo, "HEAD", sig, sig, NULL, "init", tree, 0);

	git_tree_free(tree);
	git_index_free(idx);
	git_signature_free(sig);
	git_repository_free(repo);
	return rc;
}

/* Open the repo, then look up HEAD (which opens the refdb) with the nth
 * allocation failing. Returns 0 on success, <0 on a clean error; the bug crashes. */
static int run(const char *dir, long n)
{
	git_repository *repo;
	git_reference *ref;
	int rc;

	if ((rc = git_repository_open(&repo, dir)) < 0)
		return rc;
	fail_at = n, count = 0;
	rc = git_reference_lookup(&ref, repo, "HEAD");
	fail_at = 0;
	if (rc == 0)
		git_reference_free(ref);
	git_repository_free(repo);
	printf("  alloc #%ld: %s\n", n, rc ? "clean error (handled)" : "OK");
	return rc;
}

int main(int argc, char **argv)
{
	git_allocator alloc = { x_malloc, x_realloc, free };
	char dir[] = "/tmp/06_c_XXXXXX";
	long only = argc > 1 ? atol(argv[1]) : 0, n;

	setvbuf(stdout, NULL, _IONBF, 0);
	if (!mkdtemp(dir) || git_libgit2_init() < 0 ||
	    git_libgit2_opts(GIT_OPT_SET_ALLOCATOR, &alloc) ||
	    make_repo(dir) || run(dir, 0))
		return fprintf(stderr, "06_c: setup failed\n"), 1;

	if (only)
		return run(dir, only) ? 2 : 0;

	for (n = 1; n <= 200; n++)
		run(dir, n);
	puts("06_c: no crash -- bug did NOT reproduce");
	return 1;
}
主要言語
C
スター
10.6k
フォーク
2.7k
平均マージ
11日 9時間
マージ済み PR(30日)
8

環境構築

Codespaces で開く

このプロジェクトの開発コンテナを、あなたの GitHub アカウントでブラウザ上に起動します。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

libgit2/libgit2 のほかの issue

libgit2/libgit2 の issue をすべて見る

似ている issue

C の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。