From e29e802966b7d5a44d17108704f73cca26f815e8 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 10 Apr 2017 10:31:22 +0200 Subject: [PATCH 1/7] tests: odb: make hash of fake backend configurable In the odb::backend::nonrefreshing test suite, we set up a fake backend so that we are able to determine if backend functions are called correctly. During the setup, we also parse an OID which is later on used to read out the pseudo-object. While this procedure works right now, it will create problems later when we implement hash verification for looked up objects. The current OID ("deadbeef") will not match the hash of contents we give back to the ODB layer and thus cannot be verified. Make the hash configurable so that we can simply switch the returned for single tests. --- tests/odb/backend/nonrefreshing.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/tests/odb/backend/nonrefreshing.c b/tests/odb/backend/nonrefreshing.c index b43529479..4c2362896 100644 --- a/tests/odb/backend/nonrefreshing.c +++ b/tests/odb/backend/nonrefreshing.c @@ -17,6 +17,8 @@ static git_repository *_repo; static fake_backend *_fake; static git_oid _oid; +#define HASH "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef" + static int fake_backend__exists(git_odb_backend *backend, const git_oid *oid) { fake_backend *fake; @@ -78,7 +80,6 @@ static int fake_backend__read_prefix( { fake_backend *fake; - GIT_UNUSED(out_oid); GIT_UNUSED(buffer_p); GIT_UNUSED(len_p); GIT_UNUSED(type_p); @@ -89,6 +90,7 @@ static int fake_backend__read_prefix( fake->read_prefix_calls++; + git_oid_cpy(out_oid, &_oid); *len_p = 0; *buffer_p = NULL; *type_p = GIT_OBJ_BLOB; @@ -130,7 +132,7 @@ static int build_fake_backend( return 0; } -static void setup_repository_and_backend(git_error_code error_code) +static void setup_repository_and_backend(git_error_code error_code, const char *hash) { git_odb *odb = NULL; git_odb_backend *backend = NULL; @@ -144,7 +146,7 @@ static void setup_repository_and_backend(git_error_code error_code) _fake = (fake_backend *)backend; - cl_git_pass(git_oid_fromstr(&_oid, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); + cl_git_pass(git_oid_fromstr(&_oid, hash)); } void test_odb_backend_nonrefreshing__cleanup(void) @@ -156,7 +158,7 @@ void test_odb_backend_nonrefreshing__exists_is_invoked_once_on_failure(void) { git_odb *odb; - setup_repository_and_backend(GIT_ENOTFOUND); + setup_repository_and_backend(GIT_ENOTFOUND, HASH); cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); cl_assert_equal_b(false, git_odb_exists(odb, &_oid)); @@ -168,7 +170,7 @@ void test_odb_backend_nonrefreshing__read_is_invoked_once_on_failure(void) { git_object *obj; - setup_repository_and_backend(GIT_ENOTFOUND); + setup_repository_and_backend(GIT_ENOTFOUND, HASH); cl_git_fail_with( git_object_lookup(&obj, _repo, &_oid, GIT_OBJ_ANY), @@ -181,7 +183,7 @@ void test_odb_backend_nonrefreshing__readprefix_is_invoked_once_on_failure(void) { git_object *obj; - setup_repository_and_backend(GIT_ENOTFOUND); + setup_repository_and_backend(GIT_ENOTFOUND, HASH); cl_git_fail_with( git_object_lookup_prefix(&obj, _repo, &_oid, 7, GIT_OBJ_ANY), @@ -196,7 +198,7 @@ void test_odb_backend_nonrefreshing__readheader_is_invoked_once_on_failure(void) size_t len; git_otype type; - setup_repository_and_backend(GIT_ENOTFOUND); + setup_repository_and_backend(GIT_ENOTFOUND, HASH); cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); @@ -211,7 +213,7 @@ void test_odb_backend_nonrefreshing__exists_is_invoked_once_on_success(void) { git_odb *odb; - setup_repository_and_backend(GIT_OK); + setup_repository_and_backend(GIT_OK, HASH); cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); cl_assert_equal_b(true, git_odb_exists(odb, &_oid)); @@ -223,7 +225,7 @@ void test_odb_backend_nonrefreshing__read_is_invoked_once_on_success(void) { git_object *obj; - setup_repository_and_backend(GIT_OK); + setup_repository_and_backend(GIT_OK, HASH); cl_git_pass(git_object_lookup(&obj, _repo, &_oid, GIT_OBJ_ANY)); @@ -236,7 +238,7 @@ void test_odb_backend_nonrefreshing__readprefix_is_invoked_once_on_success(void) { git_object *obj; - setup_repository_and_backend(GIT_OK); + setup_repository_and_backend(GIT_OK, EMPTY_HASH); cl_git_pass(git_object_lookup_prefix(&obj, _repo, &_oid, 7, GIT_OBJ_ANY)); @@ -251,7 +253,7 @@ void test_odb_backend_nonrefreshing__readheader_is_invoked_once_on_success(void) size_t len; git_otype type; - setup_repository_and_backend(GIT_OK); + setup_repository_and_backend(GIT_OK, HASH); cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); @@ -264,7 +266,7 @@ void test_odb_backend_nonrefreshing__read_is_invoked_once_when_revparsing_a_full { git_object *obj; - setup_repository_and_backend(GIT_ENOTFOUND); + setup_repository_and_backend(GIT_ENOTFOUND, HASH); cl_git_fail_with( git_revparse_single(&obj, _repo, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"), From 86c035526dd9eb4131ce2a3e25926c5a136b8318 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 10 Apr 2017 09:27:04 +0200 Subject: [PATCH 2/7] tests: object: create sandbox The object::lookup tests do use the "testrepo.git" repository in a read-only way, so we do not set up the repository as a sandbox but simply open it. But in a future commit, we will want to test looking up objects which are corrupted in some way, which requires us to modify the on-disk data. Doing this in a repository without creating the sandbox will modify contents of our libgit2 repository, though. Create the repository in a sandbox to avoid this. --- tests/object/lookup.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/object/lookup.c b/tests/object/lookup.c index cfa6d4678..0e492b428 100644 --- a/tests/object/lookup.c +++ b/tests/object/lookup.c @@ -6,13 +6,12 @@ static git_repository *g_repo; void test_object_lookup__initialize(void) { - cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git"))); + g_repo = cl_git_sandbox_init("testrepo.git"); } void test_object_lookup__cleanup(void) { - git_repository_free(g_repo); - g_repo = NULL; + cl_git_sandbox_cleanup(); } void test_object_lookup__lookup_wrong_type_returns_enotfound(void) From d59dabe5cbee52035810216ab118fa3f63047f2f Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 10 Apr 2017 09:00:51 +0200 Subject: [PATCH 3/7] tests: object: test looking up corrupted objects We currently have no tests which check whether we fail reading corrupted objects. Add one which modifies contents of an object stored on disk and then tries to read the object. --- tests/object/lookup.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/object/lookup.c b/tests/object/lookup.c index 0e492b428..0116ee44d 100644 --- a/tests/object/lookup.c +++ b/tests/object/lookup.c @@ -62,3 +62,33 @@ void test_object_lookup__lookup_wrong_type_eventually_returns_enotfound(void) GIT_ENOTFOUND, git_object_lookup(&object, g_repo, &oid, GIT_OBJ_TAG)); } +void test_object_lookup__lookup_corrupt_object_returns_error(void) +{ + const char *commit = "8e73b769e97678d684b809b163bebdae2911720f", + *file = "objects/8e/73b769e97678d684b809b163bebdae2911720f"; + git_buf path = GIT_BUF_INIT, contents = GIT_BUF_INIT; + git_oid oid; + git_object *object; + size_t i; + + cl_git_pass(git_oid_fromstr(&oid, commit)); + cl_git_pass(git_buf_joinpath(&path, git_repository_path(g_repo), file)); + cl_git_pass(git_futils_readbuffer(&contents, path.ptr)); + + /* Corrupt and try to read the object */ + for (i = 0; i < contents.size; i++) { + contents.ptr[i] ^= 0x1; + cl_git_pass(git_futils_writebuffer(&contents, path.ptr, O_RDWR, 0644)); + cl_git_fail(git_object_lookup(&object, g_repo, &oid, GIT_OBJ_COMMIT)); + contents.ptr[i] ^= 0x1; + } + + /* Restore original content and assert we can read the object */ + cl_git_pass(git_futils_writebuffer(&contents, path.ptr, O_RDWR, 0644)); + cl_git_pass(git_object_lookup(&object, g_repo, &oid, GIT_OBJ_COMMIT)); + + git_object_free(object); + git_buf_free(&path); + git_buf_free(&contents); +} + From 28a0741f1ae6f5e1261c8e73854dda69e7a61067 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 10 Apr 2017 09:30:08 +0200 Subject: [PATCH 4/7] odb: verify object hashes The upstream git.git project verifies objects when looking them up from disk. This avoids scenarios where objects have somehow become corrupt on disk, e.g. due to hardware failures or bit flips. While our mantra is usually to follow upstream behavior, we do not do so in this case, as we never check hashes of objects we have just read from disk. To fix this, we create a new error class `GIT_EMISMATCH` which denotes that we have looked up an object with a hashsum mismatch. `odb_read_1` will then, after having read the object from its backend, hash the object and compare the resulting hash to the expected hash. If hashes do not match, it will return an error. This obviously introduces another computation of checksums and could potentially impact performance. Note though that we usually perform I/O operations directly before doing this computation, and as such the actual overhead should be drowned out by I/O. Running our test suite seems to confirm this guess. On a Linux system with best-of-five timings, we had 21.592s with the check enabled and 21.590s with the ckeck disabled. Note though that our test suite mostly contains very small blobs only. It is expected that repositories with bigger blobs may notice an increased hit by this check. In addition to a new test, we also had to change the odb::backend::nonrefreshing test suite, which now triggers a hashsum mismatch when looking up the commit "deadbeef...". This is expected, as the fake backend allocated inside of the test will return an empty object for the OID "deadbeef...", which will obviously not hash back to "deadbeef..." again. We can simply adjust the hash to equal the hash of the empty object here to fix this test. --- include/git2/errors.h | 1 + src/odb.c | 33 ++++++++++++++++++++++++++++--- src/odb.h | 6 ++++++ tests/object/lookup.c | 22 +++++++++++++++++++++ tests/odb/backend/nonrefreshing.c | 3 ++- 5 files changed, 61 insertions(+), 4 deletions(-) diff --git a/include/git2/errors.h b/include/git2/errors.h index 71bff0f9d..6f5580253 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -54,6 +54,7 @@ typedef enum { GIT_PASSTHROUGH = -30, /**< Internal only */ GIT_ITEROVER = -31, /**< Signals end of iteration with iterator */ GIT_RETRY = -32, /**< Internal only */ + GIT_EMISMATCH = -33, /**< Hashsum mismatch in object */ } git_error_code; /** diff --git a/src/odb.c b/src/odb.c index cf321f51e..0efa23417 100644 --- a/src/odb.c +++ b/src/odb.c @@ -998,7 +998,9 @@ static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id, size_t i; git_rawobj raw; git_odb_object *object; + git_oid hashed; bool found = false; + int error; if (!only_refreshed && odb_read_hardcoded(&raw, id) == 0) found = true; @@ -1011,7 +1013,7 @@ static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id, continue; if (b->read != NULL) { - int error = b->read(&raw.data, &raw.len, &raw.type, b, id); + error = b->read(&raw.data, &raw.len, &raw.type, b, id); if (error == GIT_PASSTHROUGH || error == GIT_ENOTFOUND) continue; @@ -1025,12 +1027,24 @@ static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id, if (!found) return GIT_ENOTFOUND; + if ((error = git_odb_hash(&hashed, raw.data, raw.len, raw.type)) < 0) + goto out; + + if (!git_oid_equal(id, &hashed)) { + error = git_odb__error_mismatch(id, &hashed); + goto out; + } + giterr_clear(); if ((object = odb_object__alloc(id, &raw)) == NULL) - return -1; + goto out; *out = git_cache_store_raw(odb_cache(db), object); - return 0; + +out: + if (error) + git__free(raw.data); + return error; } int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) @@ -1411,6 +1425,19 @@ int git_odb_refresh(struct git_odb *db) return 0; } +int git_odb__error_mismatch(const git_oid *expected, const git_oid *actual) +{ + char expected_oid[GIT_OID_HEXSZ + 1], actual_oid[GIT_OID_HEXSZ + 1]; + + git_oid_tostr(expected_oid, sizeof(expected_oid), expected); + git_oid_tostr(actual_oid, sizeof(actual_oid), actual); + + giterr_set(GITERR_ODB, "object hash mismatch - expected %s but got %s", + expected_oid, actual_oid); + + return GIT_EMISMATCH; +} + int git_odb__error_notfound( const char *message, const git_oid *oid, size_t oid_len) { diff --git a/src/odb.h b/src/odb.h index 4f548bbba..78c7b0339 100644 --- a/src/odb.h +++ b/src/odb.h @@ -96,6 +96,12 @@ int git_odb__hashfd_filtered( */ int git_odb__hashlink(git_oid *out, const char *path); +/** + * Generate a GIT_EMISMATCH error for the ODB. + */ +int git_odb__error_mismatch( + const git_oid *expected, const git_oid *actual); + /* * Generate a GIT_ENOTFOUND error for the ODB. */ diff --git a/tests/object/lookup.c b/tests/object/lookup.c index 0116ee44d..ed12f917a 100644 --- a/tests/object/lookup.c +++ b/tests/object/lookup.c @@ -92,3 +92,25 @@ void test_object_lookup__lookup_corrupt_object_returns_error(void) git_buf_free(&contents); } +void test_object_lookup__lookup_object_with_wrong_hash_returns_error(void) +{ + const char *oldloose = "objects/8e/73b769e97678d684b809b163bebdae2911720f", + *newloose = "objects/8e/73b769e97678d684b809b163bebdae2911720e", + *commit = "8e73b769e97678d684b809b163bebdae2911720e"; + git_buf oldpath = GIT_BUF_INIT, newpath = GIT_BUF_INIT; + git_object *object; + git_oid oid; + + cl_git_pass(git_oid_fromstr(&oid, commit)); + + /* Copy object to another location with wrong hash */ + cl_git_pass(git_buf_joinpath(&oldpath, git_repository_path(g_repo), oldloose)); + cl_git_pass(git_buf_joinpath(&newpath, git_repository_path(g_repo), newloose)); + cl_git_pass(git_futils_cp(oldpath.ptr, newpath.ptr, 0644)); + + /* Verify that lookup fails due to a hashsum mismatch */ + cl_git_fail_with(GIT_EMISMATCH, git_object_lookup(&object, g_repo, &oid, GIT_OBJ_COMMIT)); + + git_buf_free(&oldpath); + git_buf_free(&newpath); +} diff --git a/tests/odb/backend/nonrefreshing.c b/tests/odb/backend/nonrefreshing.c index 4c2362896..f12ac741c 100644 --- a/tests/odb/backend/nonrefreshing.c +++ b/tests/odb/backend/nonrefreshing.c @@ -18,6 +18,7 @@ static fake_backend *_fake; static git_oid _oid; #define HASH "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef" +#define EMPTY_HASH "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391" static int fake_backend__exists(git_odb_backend *backend, const git_oid *oid) { @@ -225,7 +226,7 @@ void test_odb_backend_nonrefreshing__read_is_invoked_once_on_success(void) { git_object *obj; - setup_repository_and_backend(GIT_OK, HASH); + setup_repository_and_backend(GIT_OK, EMPTY_HASH); cl_git_pass(git_object_lookup(&obj, _repo, &_oid, GIT_OBJ_ANY)); From 35079f507b5287a2a4726cae7753439f753453e2 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Fri, 21 Apr 2017 07:31:56 +0000 Subject: [PATCH 5/7] odb: add option to turn off hash verification Verifying hashsums of objects we are reading from the ODB may be costly as we have to perform an additional hashsum calculation on the object. Especially when reading large objects, the penalty can be as high as 35%, as can be seen when executing the equivalent of `git cat-file` with and without verification enabled. To mitigate for this, we add a global option for libgit2 which enables the developer to turn off the verification, e.g. when he can be reasonably sure that the objects on disk won't be corrupted. --- include/git2/common.h | 8 ++++++++ src/odb.c | 14 +++++++++----- src/odb.h | 2 ++ src/settings.c | 5 +++++ tests/object/lookup.c | 5 +++++ 5 files changed, 29 insertions(+), 5 deletions(-) diff --git a/include/git2/common.h b/include/git2/common.h index 6d2092028..d83e8c3a0 100644 --- a/include/git2/common.h +++ b/include/git2/common.h @@ -182,6 +182,7 @@ typedef enum { GIT_OPT_ENABLE_SYNCHRONOUS_OBJECT_CREATION, GIT_OPT_GET_WINDOWS_SHAREMODE, GIT_OPT_SET_WINDOWS_SHAREMODE, + GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, } git_libgit2_opt_t; /** @@ -337,6 +338,13 @@ typedef enum { * > is written to permanent storage, not simply cached. This * > defaults to disabled. * + * opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, int enabled) + * + * > Enable strict verification of object hashsums when reading + * > objects from disk. This may impact performance due to an + * > additional checksum calculation on each object. This defaults + * > to enabled. + * * @param option Option key * @param ... value to set the option * @return 0 on success, <0 on failure diff --git a/src/odb.c b/src/odb.c index 0efa23417..157a3808e 100644 --- a/src/odb.c +++ b/src/odb.c @@ -31,6 +31,8 @@ #define GIT_ALTERNATES_MAX_DEPTH 5 +bool git_odb__strict_hash_verification = true; + typedef struct { git_odb_backend *backend; @@ -1027,12 +1029,14 @@ static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id, if (!found) return GIT_ENOTFOUND; - if ((error = git_odb_hash(&hashed, raw.data, raw.len, raw.type)) < 0) - goto out; + if (git_odb__strict_hash_verification) { + if ((error = git_odb_hash(&hashed, raw.data, raw.len, raw.type)) < 0) + goto out; - if (!git_oid_equal(id, &hashed)) { - error = git_odb__error_mismatch(id, &hashed); - goto out; + if (!git_oid_equal(id, &hashed)) { + error = git_odb__error_mismatch(id, &hashed); + goto out; + } } giterr_clear(); diff --git a/src/odb.h b/src/odb.h index 78c7b0339..61d687abf 100644 --- a/src/odb.h +++ b/src/odb.h @@ -20,6 +20,8 @@ #define GIT_OBJECT_DIR_MODE 0777 #define GIT_OBJECT_FILE_MODE 0444 +extern bool git_odb__strict_hash_verification; + /* DO NOT EXPORT */ typedef struct { void *data; /**< Raw, decompressed object data. */ diff --git a/src/settings.c b/src/settings.c index 07ac16a8f..169fcd51c 100644 --- a/src/settings.c +++ b/src/settings.c @@ -15,6 +15,7 @@ #include "cache.h" #include "global.h" #include "object.h" +#include "odb.h" #include "refs.h" #include "transports/smart.h" @@ -243,6 +244,10 @@ int git_libgit2_opts(int key, ...) #endif break; + case GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION: + git_odb__strict_hash_verification = (va_arg(ap, int) != 0); + break; + default: giterr_set(GITERR_INVALID, "invalid option key"); error = -1; diff --git a/tests/object/lookup.c b/tests/object/lookup.c index ed12f917a..277e2e0c0 100644 --- a/tests/object/lookup.c +++ b/tests/object/lookup.c @@ -111,6 +111,11 @@ void test_object_lookup__lookup_object_with_wrong_hash_returns_error(void) /* Verify that lookup fails due to a hashsum mismatch */ cl_git_fail_with(GIT_EMISMATCH, git_object_lookup(&object, g_repo, &oid, GIT_OBJ_COMMIT)); + /* Disable verification and try again */ + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 0)); + cl_git_pass(git_object_lookup(&object, g_repo, &oid, GIT_OBJ_COMMIT)); + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 1)); + git_buf_free(&oldpath); git_buf_free(&newpath); } From 141096202b459a70ca127b1a56a6923a75a064e4 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Fri, 28 Apr 2017 14:03:54 +0200 Subject: [PATCH 6/7] odb: improve error handling in read_prefix_1 The read_prefix_1 function has several return statements springled throughout the code. As we have to free memory upon getting an error, the free code has to be repeated at every single retrun -- which it is not, so we have a memory leak here. Refactor the code to use the typical `goto out` pattern, which will free data when an error has occurred. While we're at it, we can also improve the error message thrown when multiple ambiguous prefixes are found. It will now include the colliding prefixes. --- src/odb.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/odb.c b/src/odb.c index 157a3808e..e87114769 100644 --- a/src/odb.c +++ b/src/odb.c @@ -1099,9 +1099,9 @@ static int read_prefix_1(git_odb_object **out, git_odb *db, const git_oid *key, size_t len, bool only_refreshed) { size_t i; - int error = GIT_ENOTFOUND; + int error; git_oid found_full_oid = {{0}}; - git_rawobj raw; + git_rawobj raw = {0}; void *data = NULL; bool found = false; git_odb_object *object; @@ -1120,14 +1120,22 @@ static int read_prefix_1(git_odb_object **out, git_odb *db, continue; if (error) - return error; + goto out; git__free(data); data = raw.data; if (found && git_oid__cmp(&full_oid, &found_full_oid)) { - git__free(raw.data); - return git_odb__error_ambiguous("multiple matches for prefix"); + git_buf buf = GIT_BUF_INIT; + + git_buf_printf(&buf, "multiple matches for prefix: %s", + git_oid_tostr_s(&full_oid)); + git_buf_printf(&buf, " %s", + git_oid_tostr_s(&found_full_oid)); + + error = git_odb__error_ambiguous(buf.ptr); + git_buf_free(&buf); + goto out; } found_full_oid = full_oid; @@ -1139,10 +1147,15 @@ static int read_prefix_1(git_odb_object **out, git_odb *db, return GIT_ENOTFOUND; if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL) - return -1; + goto out; *out = git_cache_store_raw(odb_cache(db), object); - return 0; + +out: + if (error) + git__free(raw.data); + + return error; } int git_odb_read_prefix( From e0973bc0fc2b04b6bb27e4ce4db2b37e9577a75d Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Fri, 28 Apr 2017 14:05:15 +0200 Subject: [PATCH 7/7] odb: verify hashes in read_prefix_1 While the function reading an object from the complete OID already verifies OIDs, we do not yet do so for reading objects from a partial OID. Do so when strict OID verification is enabled. --- src/odb.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/odb.c b/src/odb.c index e87114769..3090ccaac 100644 --- a/src/odb.c +++ b/src/odb.c @@ -1146,6 +1146,18 @@ static int read_prefix_1(git_odb_object **out, git_odb *db, if (!found) return GIT_ENOTFOUND; + if (git_odb__strict_hash_verification) { + git_oid hash; + + if ((error = git_odb_hash(&hash, raw.data, raw.len, raw.type)) < 0) + goto out; + + if (!git_oid_equal(&found_full_oid, &hash)) { + error = git_odb__error_mismatch(&found_full_oid, &hash); + goto out; + } + } + if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL) goto out;