Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit edaffe2

Browse files
author
Edward Thomson
committed
Merge pull request libgit2#3633 from ethomson/safe_creation
Stricter object dependency checking during creation
2 parents dbee683 + f2dddf5 commit edaffe2

24 files changed

+468
-75
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ v0.23 + 1
2929
* Rebases can now be performed purely in-memory, without touching the
3030
repository's workdir.
3131

32+
* When adding objects to the index, or when creating new tree or commit
33+
objects, the inputs are validated to ensure that the dependent objects
34+
exist and are of the correct type. This object validation can be
35+
disabled with the GIT_OPT_ENABLE_STRICT_OBJECT_CREATION option.
36+
3237
### API additions
3338

3439
* `git_config_lock()` has been added, which allow for

include/git2/common.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ typedef enum {
147147
GIT_OPT_SET_TEMPLATE_PATH,
148148
GIT_OPT_SET_SSL_CERT_LOCATIONS,
149149
GIT_OPT_SET_USER_AGENT,
150+
GIT_OPT_ENABLE_STRICT_OBJECT_CREATION,
150151
} git_libgit2_opt_t;
151152

152153
/**
@@ -251,6 +252,14 @@ typedef enum {
251252
* > - `user_agent` is the value that will be delivered as the
252253
* > User-Agent header on HTTP requests.
253254
*
255+
* * opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, int enabled)
256+
*
257+
* > Enable strict input validation when creating new objects
258+
* > to ensure that all inputs to the new objects are valid. For
259+
* > example, when this is enabled, the parent(s) and tree inputs
260+
* > will be validated when creating a new commit. This defaults
261+
* > to disabled.
262+
*
254263
* @param option Option key
255264
* @param ... value to set the option
256265
* @return 0 on success, <0 on failure

src/commit.c

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "signature.h"
1818
#include "message.h"
1919
#include "refs.h"
20+
#include "object.h"
2021

2122
void git_commit__free(void *_commit)
2223
{
@@ -36,7 +37,7 @@ void git_commit__free(void *_commit)
3637
git__free(commit);
3738
}
3839

39-
int git_commit_create_from_callback(
40+
static int git_commit__create_internal(
4041
git_oid *id,
4142
git_repository *repo,
4243
const char *update_ref,
@@ -46,7 +47,8 @@ int git_commit_create_from_callback(
4647
const char *message,
4748
const git_oid *tree,
4849
git_commit_parent_callback parent_cb,
49-
void *parent_payload)
50+
void *parent_payload,
51+
bool validate)
5052
{
5153
git_reference *ref = NULL;
5254
int error = 0, matched_parent = 0;
@@ -58,6 +60,9 @@ int git_commit_create_from_callback(
5860

5961
assert(id && repo && tree && parent_cb);
6062

63+
if (validate && !git_object__is_valid(repo, tree, GIT_OBJ_TREE))
64+
return -1;
65+
6166
if (update_ref) {
6267
error = git_reference_lookup_resolved(&ref, repo, update_ref, 10);
6368
if (error < 0 && error != GIT_ENOTFOUND)
@@ -71,6 +76,11 @@ int git_commit_create_from_callback(
7176
git_oid__writebuf(&commit, "tree ", tree);
7277

7378
while ((parent = parent_cb(i, parent_payload)) != NULL) {
79+
if (validate && !git_object__is_valid(repo, parent, GIT_OBJ_COMMIT)) {
80+
error = -1;
81+
goto on_error;
82+
}
83+
7484
git_oid__writebuf(&commit, "parent ", parent);
7585
if (i == 0 && current_id && git_oid_equal(current_id, parent))
7686
matched_parent = 1;
@@ -114,10 +124,26 @@ int git_commit_create_from_callback(
114124

115125
on_error:
116126
git_buf_free(&commit);
117-
giterr_set(GITERR_OBJECT, "Failed to create commit.");
118127
return -1;
119128
}
120129

130+
int git_commit_create_from_callback(
131+
git_oid *id,
132+
git_repository *repo,
133+
const char *update_ref,
134+
const git_signature *author,
135+
const git_signature *committer,
136+
const char *message_encoding,
137+
const char *message,
138+
const git_oid *tree,
139+
git_commit_parent_callback parent_cb,
140+
void *parent_payload)
141+
{
142+
return git_commit__create_internal(
143+
id, repo, update_ref, author, committer, message_encoding, message,
144+
tree, parent_cb, parent_payload, true);
145+
}
146+
121147
typedef struct {
122148
size_t total;
123149
va_list args;
@@ -153,10 +179,10 @@ int git_commit_create_v(
153179
data.total = parent_count;
154180
va_start(data.args, parent_count);
155181

156-
error = git_commit_create_from_callback(
182+
error = git_commit__create_internal(
157183
id, repo, update_ref, author, committer,
158184
message_encoding, message, git_tree_id(tree),
159-
commit_parent_from_varargs, &data);
185+
commit_parent_from_varargs, &data, false);
160186

161187
va_end(data.args);
162188
return error;
@@ -187,10 +213,10 @@ int git_commit_create_from_ids(
187213
{
188214
commit_parent_oids data = { parent_count, parents };
189215

190-
return git_commit_create_from_callback(
216+
return git_commit__create_internal(
191217
id, repo, update_ref, author, committer,
192218
message_encoding, message, tree,
193-
commit_parent_from_ids, &data);
219+
commit_parent_from_ids, &data, true);
194220
}
195221

196222
typedef struct {
@@ -227,10 +253,10 @@ int git_commit_create(
227253

228254
assert(tree && git_tree_owner(tree) == repo);
229255

230-
return git_commit_create_from_callback(
256+
return git_commit__create_internal(
231257
id, repo, update_ref, author, committer,
232258
message_encoding, message, git_tree_id(tree),
233-
commit_parent_from_array, &data);
259+
commit_parent_from_array, &data, false);
234260
}
235261

236262
static const git_oid *commit_parent_for_amend(size_t curr, void *payload)
@@ -290,9 +316,9 @@ int git_commit_amend(
290316
}
291317
}
292318

293-
error = git_commit_create_from_callback(
319+
error = git_commit__create_internal(
294320
id, repo, NULL, author, committer, message_encoding, message,
295-
&tree_id, commit_parent_for_amend, (void *)commit_to_amend);
321+
&tree_id, commit_parent_for_amend, (void *)commit_to_amend, false);
296322

297323
if (!error && update_ref) {
298324
error = git_reference__update_for_commit(

src/index.c

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1245,17 +1245,22 @@ static void index_existing_and_best(
12451245
* it, then it will return an error **and also free the entry**. When
12461246
* it replaces an existing entry, it will update the entry_ptr with the
12471247
* actual entry in the index (and free the passed in one).
1248+
*
12481249
* trust_path is whether we use the given path, or whether (on case
12491250
* insensitive systems only) we try to canonicalize the given path to
12501251
* be within an existing directory.
1252+
*
12511253
* trust_mode is whether we trust the mode in entry_ptr.
1254+
*
1255+
* trust_id is whether we trust the id or it should be validated.
12521256
*/
12531257
static int index_insert(
12541258
git_index *index,
12551259
git_index_entry **entry_ptr,
12561260
int replace,
12571261
bool trust_path,
1258-
bool trust_mode)
1262+
bool trust_mode,
1263+
bool trust_id)
12591264
{
12601265
int error = 0;
12611266
size_t path_length, position;
@@ -1288,6 +1293,15 @@ static int index_insert(
12881293
if (!trust_path)
12891294
error = canonicalize_directory_path(index, entry, best);
12901295

1296+
/* ensure that the given id exists (unless it's a submodule) */
1297+
if (!error && !trust_id && INDEX_OWNER(index) &&
1298+
(entry->mode & GIT_FILEMODE_COMMIT) != GIT_FILEMODE_COMMIT) {
1299+
1300+
if (!git_object__is_valid(INDEX_OWNER(index), &entry->id,
1301+
git_object__type_from_filemode(entry->mode)))
1302+
error = -1;
1303+
}
1304+
12911305
/* look for tree / blob name collisions, removing conflicts if requested */
12921306
if (!error)
12931307
error = check_file_directory_collision(index, entry, position, replace);
@@ -1395,7 +1409,7 @@ int git_index_add_frombuffer(
13951409
git_oid_cpy(&entry->id, &id);
13961410
entry->file_size = len;
13971411

1398-
if ((error = index_insert(index, &entry, 1, true, true)) < 0)
1412+
if ((error = index_insert(index, &entry, 1, true, true, true)) < 0)
13991413
return error;
14001414

14011415
/* Adding implies conflict was resolved, move conflict entries to REUC */
@@ -1454,7 +1468,7 @@ int git_index_add_bypath(git_index *index, const char *path)
14541468
assert(index && path);
14551469

14561470
if ((ret = index_entry_init(&entry, index, path)) == 0)
1457-
ret = index_insert(index, &entry, 1, false, false);
1471+
ret = index_insert(index, &entry, 1, false, false, true);
14581472

14591473
/* If we were given a directory, let's see if it's a submodule */
14601474
if (ret < 0 && ret != GIT_EDIRECTORY)
@@ -1480,7 +1494,7 @@ int git_index_add_bypath(git_index *index, const char *path)
14801494
if ((ret = add_repo_as_submodule(&entry, index, path)) < 0)
14811495
return ret;
14821496

1483-
if ((ret = index_insert(index, &entry, 1, false, false)) < 0)
1497+
if ((ret = index_insert(index, &entry, 1, false, false, true)) < 0)
14841498
return ret;
14851499
} else if (ret < 0) {
14861500
return ret;
@@ -1569,7 +1583,7 @@ int git_index_add(git_index *index, const git_index_entry *source_entry)
15691583
}
15701584

15711585
if ((ret = index_entry_dup(&entry, index, source_entry)) < 0 ||
1572-
(ret = index_insert(index, &entry, 1, true, true)) < 0)
1586+
(ret = index_insert(index, &entry, 1, true, true, false)) < 0)
15731587
return ret;
15741588

15751589
git_tree_cache_invalidate_path(index->tree, entry->path);
@@ -1731,7 +1745,7 @@ int git_index_conflict_add(git_index *index,
17311745
/* Make sure stage is correct */
17321746
GIT_IDXENTRY_STAGE_SET(entries[i], i + 1);
17331747

1734-
if ((ret = index_insert(index, &entries[i], 1, true, true)) < 0)
1748+
if ((ret = index_insert(index, &entries[i], 1, true, true, false)) < 0)
17351749
goto on_error;
17361750

17371751
entries[i] = NULL; /* don't free if later entry fails */

src/object.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#include "blob.h"
1515
#include "tag.h"
1616

17+
bool git_object__strict_input_validation = true;
18+
1719
typedef struct {
1820
const char *str; /* type name string */
1921
size_t size; /* size in bytes of the object structure */
@@ -465,3 +467,27 @@ int git_object_short_id(git_buf *out, const git_object *obj)
465467
return error;
466468
}
467469

470+
bool git_object__is_valid(
471+
git_repository *repo, const git_oid *id, git_otype expected_type)
472+
{
473+
git_odb *odb;
474+
git_otype actual_type;
475+
size_t len;
476+
int error;
477+
478+
if (!git_object__strict_input_validation)
479+
return true;
480+
481+
if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
482+
(error = git_odb_read_header(&len, &actual_type, odb, id)) < 0)
483+
return false;
484+
485+
if (expected_type != GIT_OBJ_ANY && expected_type != actual_type) {
486+
giterr_set(GITERR_INVALID,
487+
"the requested type does not match the type in the ODB");
488+
return false;
489+
}
490+
491+
return true;
492+
}
493+

src/object.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
#ifndef INCLUDE_object_h__
88
#define INCLUDE_object_h__
99

10+
#include "repository.h"
11+
12+
extern bool git_object__strict_input_validation;
13+
1014
/** Base git object for inheritance */
1115
struct git_object {
1216
git_cached_obj cached;
@@ -28,4 +32,23 @@ int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end
2832

2933
void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid);
3034

35+
bool git_object__is_valid(
36+
git_repository *repo, const git_oid *id, git_otype expected_type);
37+
38+
GIT_INLINE(git_otype) git_object__type_from_filemode(git_filemode_t mode)
39+
{
40+
switch (mode) {
41+
case GIT_FILEMODE_TREE:
42+
return GIT_OBJ_TREE;
43+
case GIT_FILEMODE_COMMIT:
44+
return GIT_OBJ_COMMIT;
45+
case GIT_FILEMODE_BLOB:
46+
case GIT_FILEMODE_BLOB_EXECUTABLE:
47+
case GIT_FILEMODE_LINK:
48+
return GIT_OBJ_BLOB;
49+
default:
50+
return GIT_OBJ_BAD;
51+
}
52+
}
53+
3154
#endif

src/refs.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -377,15 +377,9 @@ static int reference__create(
377377
return error;
378378

379379
if (oid != NULL) {
380-
git_odb *odb;
381-
382380
assert(symbolic == NULL);
383381

384-
/* Sanity check the reference being created - target must exist. */
385-
if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
386-
return error;
387-
388-
if (!git_odb_exists(odb, oid)) {
382+
if (!git_object__is_valid(repo, oid, GIT_OBJ_ANY)) {
389383
giterr_set(GITERR_REFERENCE,
390384
"Target OID for the reference doesn't exist on the repository");
391385
return -1;

src/settings.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "sysdir.h"
1515
#include "cache.h"
1616
#include "global.h"
17+
#include "object.h"
1718

1819
void git_libgit2_version(int *major, int *minor, int *rev)
1920
{
@@ -181,6 +182,11 @@ int git_libgit2_opts(int key, ...)
181182
}
182183

183184
break;
185+
186+
case GIT_OPT_ENABLE_STRICT_OBJECT_CREATION:
187+
git_object__strict_input_validation = (va_arg(ap, int) != 0);
188+
break;
189+
184190
default:
185191
giterr_set(GITERR_INVALID, "invalid option key");
186192
error = -1;

src/tree.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,18 @@ int git_treebuilder_new(
726726
return -1;
727727
}
728728

729+
static git_otype otype_from_mode(git_filemode_t filemode)
730+
{
731+
switch (filemode) {
732+
case GIT_FILEMODE_TREE:
733+
return GIT_OBJ_TREE;
734+
case GIT_FILEMODE_COMMIT:
735+
return GIT_OBJ_COMMIT;
736+
default:
737+
return GIT_OBJ_BLOB;
738+
}
739+
}
740+
729741
int git_treebuilder_insert(
730742
const git_tree_entry **entry_out,
731743
git_treebuilder *bld,
@@ -745,6 +757,9 @@ int git_treebuilder_insert(
745757
if (!valid_entry_name(bld->repo, filename))
746758
return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
747759

760+
if (!git_object__is_valid(bld->repo, id, otype_from_mode(filemode)))
761+
return tree_error("Failed to insert entry; invalid object specified", filename);
762+
748763
pos = git_strmap_lookup_index(bld->map, filename);
749764
if (git_strmap_valid_index(bld->map, pos)) {
750765
entry = git_strmap_value_at(bld->map, pos);

0 commit comments

Comments
 (0)