blame_git: detect memory allocation errors The code in "blame_git.c" was mostly imported from git.git with only minor changes. One of these changes was to use our own allocators instead of git's `xmalloc`, but there's a subtle difference: `xmalloc` would abort the program if unable to allocate any memory, bit `git__malloc` doesn't. As we didn't check for memory allocation errors in some places, we might inadvertently dereference a `NULL` pointer in out-of-memory situations. Convert multiple functions to return proper error codes and add calls to `GIT_ERROR_CHECK_ALLOC` to fix this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
diff --git a/src/blame_git.c b/src/blame_git.c
index 06e7d64..a9157c4 100644
--- a/src/blame_git.c
+++ b/src/blame_git.c
@@ -219,7 +219,7 @@ static void dup_entry(git_blame__entry *dst, git_blame__entry *src)
* split_overlap() divided an existing blame e into up to three parts in split.
* Adjust the linked list of blames in the scoreboard to reflect the split.
*/
-static void split_blame(git_blame *blame, git_blame__entry *split, git_blame__entry *e)
+static int split_blame(git_blame *blame, git_blame__entry *split, git_blame__entry *e)
{
git_blame__entry *new_entry;
@@ -229,11 +229,13 @@ static void split_blame(git_blame *blame, git_blame__entry *split, git_blame__en
/* The last part -- me */
new_entry = git__malloc(sizeof(*new_entry));
+ GIT_ERROR_CHECK_ALLOC(new_entry);
memcpy(new_entry, &(split[2]), sizeof(git_blame__entry));
add_blame_entry(blame, new_entry);
/* ... and the middle part -- parent */
new_entry = git__malloc(sizeof(*new_entry));
+ GIT_ERROR_CHECK_ALLOC(new_entry);
memcpy(new_entry, &(split[1]), sizeof(git_blame__entry));
add_blame_entry(blame, new_entry);
} else if (!split[0].suspect && !split[2].suspect) {
@@ -246,15 +248,19 @@ static void split_blame(git_blame *blame, git_blame__entry *split, git_blame__en
/* me and then parent */
dup_entry(e, &split[0]);
new_entry = git__malloc(sizeof(*new_entry));
+ GIT_ERROR_CHECK_ALLOC(new_entry);
memcpy(new_entry, &(split[1]), sizeof(git_blame__entry));
add_blame_entry(blame, new_entry);
} else {
/* parent and then me */
dup_entry(e, &split[1]);
new_entry = git__malloc(sizeof(*new_entry));
+ GIT_ERROR_CHECK_ALLOC(new_entry);
memcpy(new_entry, &(split[2]), sizeof(git_blame__entry));
add_blame_entry(blame, new_entry);
}
+
+ return 0;
}
/*
@@ -272,7 +278,7 @@ static void decref_split(git_blame__entry *split)
* Helper for blame_chunk(). blame_entry e is known to overlap with the patch
* hunk; split it and pass blame to the parent.
*/
-static void blame_overlap(
+static int blame_overlap(
git_blame *blame,
git_blame__entry *e,
size_t tlno,
@@ -284,8 +290,11 @@ static void blame_overlap(
split_overlap(split, e, tlno, plno, same, parent);
if (split[1].suspect)
- split_blame(blame, split, e);
+ if (split_blame(blame, split, e) < 0)
+ return -1;
decref_split(split);
+
+ return 0;
}
/*
@@ -293,7 +302,7 @@ static void blame_overlap(
* e and its parent. Find and split the overlap, and pass blame to the
* overlapping part to the parent.
*/
-static void blame_chunk(
+static int blame_chunk(
git_blame *blame,
size_t tlno,
size_t plno,
@@ -309,9 +318,12 @@ static void blame_chunk(
if (same <= e->s_lno)
continue;
if (tlno < e->s_lno + e->num_lines) {
- blame_overlap(blame, e, tlno, plno, same, parent);
+ if (blame_overlap(blame, e, tlno, plno, same, parent) < 0)
+ return -1;
}
}
+
+ return 0;
}
static int my_emit(
@@ -321,7 +333,8 @@ static int my_emit(
{
blame_chunk_cb_data *d = (blame_chunk_cb_data *)cb_data;
- blame_chunk(d->blame, d->tlno, d->plno, start_b, d->target, d->parent);
+ if (blame_chunk(d->blame, d->tlno, d->plno, start_b, d->target, d->parent) < 0)
+ return -1;
d->plno = start_a + count_a;
d->tlno = start_b + count_b;
@@ -400,7 +413,8 @@ static int pass_blame_to_parent(
return -1;
/* The reset (i.e. anything after tlno) are the same as the parent */
- blame_chunk(blame, d.tlno, d.plno, last_in_target, target, parent);
+ if (blame_chunk(blame, d.tlno, d.plno, last_in_target, target, parent) < 0)
+ return -1;
return 0;
}