Skip to content

Commit 30c8e26

Browse files
committed
Merge pull request libgit2#3521 from pks-t/blame-line-overflow
Line count overflow in git_blame_hunk and git_blame__entry
2 parents 6aa06b6 + 944dbd1 commit 30c8e26

14 files changed

+60
-44
lines changed

include/git2/blame.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ typedef struct git_blame_options {
7474
uint16_t min_match_characters;
7575
git_oid newest_commit;
7676
git_oid oldest_commit;
77-
uint32_t min_line;
78-
uint32_t max_line;
77+
size_t min_line;
78+
size_t max_line;
7979
} git_blame_options;
8080

8181
#define GIT_BLAME_OPTIONS_VERSION 1
@@ -113,15 +113,15 @@ GIT_EXTERN(int) git_blame_init_options(
113113
* root, or the commit specified in git_blame_options.oldest_commit)
114114
*/
115115
typedef struct git_blame_hunk {
116-
uint16_t lines_in_hunk;
116+
size_t lines_in_hunk;
117117

118118
git_oid final_commit_id;
119-
uint16_t final_start_line_number;
119+
size_t final_start_line_number;
120120
git_signature *final_signature;
121121

122122
git_oid orig_commit_id;
123123
const char *orig_path;
124-
uint16_t orig_start_line_number;
124+
size_t orig_start_line_number;
125125
git_signature *orig_signature;
126126

127127
char boundary;
@@ -156,7 +156,7 @@ GIT_EXTERN(const git_blame_hunk*) git_blame_get_hunk_byindex(
156156
*/
157157
GIT_EXTERN(const git_blame_hunk*) git_blame_get_hunk_byline(
158158
git_blame *blame,
159-
uint32_t lineno);
159+
size_t lineno);
160160

161161
/**
162162
* Get the blame for a single file.

src/blame.c

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ static int hunk_byfinalline_search_cmp(const void *key, const void *entry)
2323
git_blame_hunk *hunk = (git_blame_hunk*)entry;
2424

2525
size_t lineno = *(size_t*)key;
26-
size_t lines_in_hunk = (size_t)hunk->lines_in_hunk;
27-
size_t final_start_line_number = (size_t)hunk->final_start_line_number;
26+
size_t lines_in_hunk = hunk->lines_in_hunk;
27+
size_t final_start_line_number = hunk->final_start_line_number;
2828

2929
if (lineno < final_start_line_number)
3030
return -1;
@@ -44,7 +44,7 @@ static int hunk_cmp(const void *_a, const void *_b)
4444

4545
static bool hunk_ends_at_or_before_line(git_blame_hunk *hunk, size_t line)
4646
{
47-
return line >= (size_t)(hunk->final_start_line_number + hunk->lines_in_hunk - 1);
47+
return line >= (hunk->final_start_line_number + hunk->lines_in_hunk - 1);
4848
}
4949

5050
static bool hunk_starts_at_or_after_line(git_blame_hunk *hunk, size_t line)
@@ -53,9 +53,9 @@ static bool hunk_starts_at_or_after_line(git_blame_hunk *hunk, size_t line)
5353
}
5454

5555
static git_blame_hunk* new_hunk(
56-
uint16_t start,
57-
uint16_t lines,
58-
uint16_t orig_start,
56+
size_t start,
57+
size_t lines,
58+
size_t orig_start,
5959
const char *path)
6060
{
6161
git_blame_hunk *hunk = git__calloc(1, sizeof(git_blame_hunk));
@@ -166,9 +166,9 @@ const git_blame_hunk *git_blame_get_hunk_byindex(git_blame *blame, uint32_t inde
166166
return (git_blame_hunk*)git_vector_get(&blame->hunks, index);
167167
}
168168

169-
const git_blame_hunk *git_blame_get_hunk_byline(git_blame *blame, uint32_t lineno)
169+
const git_blame_hunk *git_blame_get_hunk_byline(git_blame *blame, size_t lineno)
170170
{
171-
size_t i, new_lineno = (size_t)lineno;
171+
size_t i, new_lineno = lineno;
172172
assert(blame);
173173

174174
if (!git_vector_bsearch2(&i, &blame->hunks, hunk_byfinalline_search_cmp, &new_lineno)) {
@@ -223,8 +223,8 @@ static git_blame_hunk *split_hunk_in_vector(
223223
}
224224

225225
new_line_count = hunk->lines_in_hunk - rel_line;
226-
nh = new_hunk((uint16_t)(hunk->final_start_line_number+rel_line), (uint16_t)new_line_count,
227-
(uint16_t)(hunk->orig_start_line_number+rel_line), hunk->orig_path);
226+
nh = new_hunk(hunk->final_start_line_number + rel_line, new_line_count,
227+
hunk->orig_start_line_number + rel_line, hunk->orig_path);
228228

229229
if (!nh)
230230
return NULL;
@@ -233,7 +233,7 @@ static git_blame_hunk *split_hunk_in_vector(
233233
git_oid_cpy(&nh->orig_commit_id, &hunk->orig_commit_id);
234234

235235
/* Adjust hunk that was split */
236-
hunk->lines_in_hunk -= (uint16_t)new_line_count;
236+
hunk->lines_in_hunk -= new_line_count;
237237
git_vector_insert_sorted(vec, nh, NULL);
238238
{
239239
git_blame_hunk *ret = return_new ? nh : hunk;
@@ -442,7 +442,7 @@ static int buffer_line_cb(
442442
} else {
443443
/* Create a new buffer-blame hunk with this line */
444444
shift_hunks_by(&blame->hunks, blame->current_diff_line, 1);
445-
blame->current_hunk = new_hunk((uint16_t)blame->current_diff_line, 1, 0, blame->path);
445+
blame->current_hunk = new_hunk(blame->current_diff_line, 1, 0, blame->path);
446446
GITERR_CHECK_ALLOC(blame->current_hunk);
447447

448448
git_vector_insert_sorted(&blame->hunks, blame->current_hunk, NULL);

src/blame.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ typedef struct git_blame__entry {
3131
/* the first line of this group in the final image;
3232
* internally all line numbers are 0 based.
3333
*/
34-
int lno;
34+
size_t lno;
3535

3636
/* how many lines this group has */
37-
int num_lines;
37+
size_t num_lines;
3838

3939
/* the commit that introduced this group into the final image */
4040
git_blame__origin *suspect;
@@ -51,7 +51,7 @@ typedef struct git_blame__entry {
5151
/* the line number of the first line of this group in the
5252
* suspect's file; internally all line numbers are 0 based.
5353
*/
54-
int s_lno;
54+
size_t s_lno;
5555

5656
/* how significant this entry is -- cached to avoid
5757
* scanning the lines over and over.

src/blame_git.c

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -93,18 +93,25 @@ static bool same_suspect(git_blame__origin *a, git_blame__origin *b)
9393
}
9494

9595
/* find the line number of the last line the target is suspected for */
96-
static int find_last_in_target(git_blame *blame, git_blame__origin *target)
96+
static bool find_last_in_target(size_t *out, git_blame *blame, git_blame__origin *target)
9797
{
9898
git_blame__entry *e;
99-
int last_in_target = -1;
99+
size_t last_in_target = 0;
100+
bool found = false;
101+
102+
*out = 0;
100103

101104
for (e=blame->ent; e; e=e->next) {
102105
if (e->guilty || !same_suspect(e->suspect, target))
103106
continue;
104-
if (last_in_target < e->s_lno + e->num_lines)
107+
if (last_in_target < e->s_lno + e->num_lines) {
108+
found = true;
105109
last_in_target = e->s_lno + e->num_lines;
110+
}
106111
}
107-
return last_in_target;
112+
113+
*out = last_in_target;
114+
return found;
108115
}
109116

110117
/*
@@ -122,9 +129,9 @@ static int find_last_in_target(git_blame *blame, git_blame__origin *target)
122129
* to be blamed for the parent, and after that portion.
123130
*/
124131
static void split_overlap(git_blame__entry *split, git_blame__entry *e,
125-
int tlno, int plno, int same, git_blame__origin *parent)
132+
size_t tlno, size_t plno, size_t same, git_blame__origin *parent)
126133
{
127-
int chunk_end_lno;
134+
size_t chunk_end_lno;
128135

129136
if (e->s_lno < tlno) {
130137
/* there is a pre-chunk part not blamed on the parent */
@@ -265,9 +272,9 @@ static void decref_split(git_blame__entry *split)
265272
static void blame_overlap(
266273
git_blame *blame,
267274
git_blame__entry *e,
268-
int tlno,
269-
int plno,
270-
int same,
275+
size_t tlno,
276+
size_t plno,
277+
size_t same,
271278
git_blame__origin *parent)
272279
{
273280
git_blame__entry split[3] = {{0}};
@@ -285,9 +292,9 @@ static void blame_overlap(
285292
*/
286293
static void blame_chunk(
287294
git_blame *blame,
288-
int tlno,
289-
int plno,
290-
int same,
295+
size_t tlno,
296+
size_t plno,
297+
size_t same,
291298
git_blame__origin *target,
292299
git_blame__origin *parent)
293300
{
@@ -314,7 +321,7 @@ static int my_emit(
314321
blame_chunk(d->blame, d->tlno, d->plno, start_b, d->target, d->parent);
315322
d->plno = start_a + count_a;
316323
d->tlno = start_b + count_b;
317-
324+
318325
return 0;
319326
}
320327

@@ -376,12 +383,11 @@ static int pass_blame_to_parent(
376383
git_blame__origin *target,
377384
git_blame__origin *parent)
378385
{
379-
int last_in_target;
386+
size_t last_in_target;
380387
mmfile_t file_p, file_o;
381388
blame_chunk_cb_data d = { blame, target, parent, 0, 0 };
382389

383-
last_in_target = find_last_in_target(blame, target);
384-
if (last_in_target < 0)
390+
if (!find_last_in_target(&last_in_target, blame, target))
385391
return 1; /* nothing remains for this target */
386392

387393
fill_origin_blob(parent, &file_p);

tests/blame/blame_helpers.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ void hunk_message(size_t idx, const git_blame_hunk *hunk, const char *fmt, ...)
44
{
55
va_list arglist;
66

7-
printf("Hunk %"PRIuZ" (line %d +%d): ", idx,
7+
printf("Hunk %"PRIuZ" (line %"PRIuZ" +%"PRIuZ"): ", idx,
88
hunk->final_start_line_number, hunk->lines_in_hunk-1);
99

1010
va_start(arglist, fmt);
@@ -15,7 +15,7 @@ void hunk_message(size_t idx, const git_blame_hunk *hunk, const char *fmt, ...)
1515
}
1616

1717
void check_blame_hunk_index(git_repository *repo, git_blame *blame, int idx,
18-
int start_line, int len, char boundary, const char *commit_id, const char *orig_path)
18+
size_t start_line, size_t len, char boundary, const char *commit_id, const char *orig_path)
1919
{
2020
char expected[GIT_OID_HEXSZ+1] = {0}, actual[GIT_OID_HEXSZ+1] = {0};
2121
const git_blame_hunk *hunk = git_blame_get_hunk_byindex(blame, idx);

tests/blame/blame_helpers.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,8 @@ void check_blame_hunk_index(
77
git_repository *repo,
88
git_blame *blame,
99
int idx,
10-
int start_line,
11-
int len,
10+
size_t start_line,
11+
size_t len,
1212
char boundary,
1313
const char *commit_id,
1414
const char *orig_path);
15-
16-

tests/blame/simple.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,18 @@ void test_blame_simple__can_restrict_lines_both(void)
281281
check_blame_hunk_index(g_repo, g_blame, 2, 6, 2, 0, "63d671eb", "b.txt");
282282
}
283283

284+
void test_blame_simple__can_blame_huge_file(void)
285+
{
286+
git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
287+
288+
cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
289+
290+
cl_git_pass(git_blame_file(&g_blame, g_repo, "huge.txt", &opts));
291+
cl_assert_equal_i(2, git_blame_get_hunk_count(g_blame));
292+
check_blame_hunk_index(g_repo, g_blame, 0, 1, 65536, 0, "4eecfea", "huge.txt");
293+
check_blame_hunk_index(g_repo, g_blame, 1, 65537, 1, 0, "6653ff4", "huge.txt");
294+
}
295+
284296
/*
285297
* $ git blame -n branch_file.txt be3563a..HEAD
286298
* orig line no final line no
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)