mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-29 22:25:04 +00:00
Merge pull request #3521 from pks-t/blame-line-overflow
Line count overflow in git_blame_hunk and git_blame__entry
This commit is contained in:
commit
30c8e26074
@ -74,8 +74,8 @@ typedef struct git_blame_options {
|
|||||||
uint16_t min_match_characters;
|
uint16_t min_match_characters;
|
||||||
git_oid newest_commit;
|
git_oid newest_commit;
|
||||||
git_oid oldest_commit;
|
git_oid oldest_commit;
|
||||||
uint32_t min_line;
|
size_t min_line;
|
||||||
uint32_t max_line;
|
size_t max_line;
|
||||||
} git_blame_options;
|
} git_blame_options;
|
||||||
|
|
||||||
#define GIT_BLAME_OPTIONS_VERSION 1
|
#define GIT_BLAME_OPTIONS_VERSION 1
|
||||||
@ -113,15 +113,15 @@ GIT_EXTERN(int) git_blame_init_options(
|
|||||||
* root, or the commit specified in git_blame_options.oldest_commit)
|
* root, or the commit specified in git_blame_options.oldest_commit)
|
||||||
*/
|
*/
|
||||||
typedef struct git_blame_hunk {
|
typedef struct git_blame_hunk {
|
||||||
uint16_t lines_in_hunk;
|
size_t lines_in_hunk;
|
||||||
|
|
||||||
git_oid final_commit_id;
|
git_oid final_commit_id;
|
||||||
uint16_t final_start_line_number;
|
size_t final_start_line_number;
|
||||||
git_signature *final_signature;
|
git_signature *final_signature;
|
||||||
|
|
||||||
git_oid orig_commit_id;
|
git_oid orig_commit_id;
|
||||||
const char *orig_path;
|
const char *orig_path;
|
||||||
uint16_t orig_start_line_number;
|
size_t orig_start_line_number;
|
||||||
git_signature *orig_signature;
|
git_signature *orig_signature;
|
||||||
|
|
||||||
char boundary;
|
char boundary;
|
||||||
@ -156,7 +156,7 @@ GIT_EXTERN(const git_blame_hunk*) git_blame_get_hunk_byindex(
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(const git_blame_hunk*) git_blame_get_hunk_byline(
|
GIT_EXTERN(const git_blame_hunk*) git_blame_get_hunk_byline(
|
||||||
git_blame *blame,
|
git_blame *blame,
|
||||||
uint32_t lineno);
|
size_t lineno);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the blame for a single file.
|
* Get the blame for a single file.
|
||||||
|
24
src/blame.c
24
src/blame.c
@ -23,8 +23,8 @@ static int hunk_byfinalline_search_cmp(const void *key, const void *entry)
|
|||||||
git_blame_hunk *hunk = (git_blame_hunk*)entry;
|
git_blame_hunk *hunk = (git_blame_hunk*)entry;
|
||||||
|
|
||||||
size_t lineno = *(size_t*)key;
|
size_t lineno = *(size_t*)key;
|
||||||
size_t lines_in_hunk = (size_t)hunk->lines_in_hunk;
|
size_t lines_in_hunk = hunk->lines_in_hunk;
|
||||||
size_t final_start_line_number = (size_t)hunk->final_start_line_number;
|
size_t final_start_line_number = hunk->final_start_line_number;
|
||||||
|
|
||||||
if (lineno < final_start_line_number)
|
if (lineno < final_start_line_number)
|
||||||
return -1;
|
return -1;
|
||||||
@ -44,7 +44,7 @@ static int hunk_cmp(const void *_a, const void *_b)
|
|||||||
|
|
||||||
static bool hunk_ends_at_or_before_line(git_blame_hunk *hunk, size_t line)
|
static bool hunk_ends_at_or_before_line(git_blame_hunk *hunk, size_t line)
|
||||||
{
|
{
|
||||||
return line >= (size_t)(hunk->final_start_line_number + hunk->lines_in_hunk - 1);
|
return line >= (hunk->final_start_line_number + hunk->lines_in_hunk - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool hunk_starts_at_or_after_line(git_blame_hunk *hunk, size_t line)
|
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static git_blame_hunk* new_hunk(
|
static git_blame_hunk* new_hunk(
|
||||||
uint16_t start,
|
size_t start,
|
||||||
uint16_t lines,
|
size_t lines,
|
||||||
uint16_t orig_start,
|
size_t orig_start,
|
||||||
const char *path)
|
const char *path)
|
||||||
{
|
{
|
||||||
git_blame_hunk *hunk = git__calloc(1, sizeof(git_blame_hunk));
|
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
|
|||||||
return (git_blame_hunk*)git_vector_get(&blame->hunks, index);
|
return (git_blame_hunk*)git_vector_get(&blame->hunks, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
const git_blame_hunk *git_blame_get_hunk_byline(git_blame *blame, uint32_t lineno)
|
const git_blame_hunk *git_blame_get_hunk_byline(git_blame *blame, size_t lineno)
|
||||||
{
|
{
|
||||||
size_t i, new_lineno = (size_t)lineno;
|
size_t i, new_lineno = lineno;
|
||||||
assert(blame);
|
assert(blame);
|
||||||
|
|
||||||
if (!git_vector_bsearch2(&i, &blame->hunks, hunk_byfinalline_search_cmp, &new_lineno)) {
|
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(
|
|||||||
}
|
}
|
||||||
|
|
||||||
new_line_count = hunk->lines_in_hunk - rel_line;
|
new_line_count = hunk->lines_in_hunk - rel_line;
|
||||||
nh = new_hunk((uint16_t)(hunk->final_start_line_number+rel_line), (uint16_t)new_line_count,
|
nh = new_hunk(hunk->final_start_line_number + rel_line, new_line_count,
|
||||||
(uint16_t)(hunk->orig_start_line_number+rel_line), hunk->orig_path);
|
hunk->orig_start_line_number + rel_line, hunk->orig_path);
|
||||||
|
|
||||||
if (!nh)
|
if (!nh)
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -233,7 +233,7 @@ static git_blame_hunk *split_hunk_in_vector(
|
|||||||
git_oid_cpy(&nh->orig_commit_id, &hunk->orig_commit_id);
|
git_oid_cpy(&nh->orig_commit_id, &hunk->orig_commit_id);
|
||||||
|
|
||||||
/* Adjust hunk that was split */
|
/* Adjust hunk that was split */
|
||||||
hunk->lines_in_hunk -= (uint16_t)new_line_count;
|
hunk->lines_in_hunk -= new_line_count;
|
||||||
git_vector_insert_sorted(vec, nh, NULL);
|
git_vector_insert_sorted(vec, nh, NULL);
|
||||||
{
|
{
|
||||||
git_blame_hunk *ret = return_new ? nh : hunk;
|
git_blame_hunk *ret = return_new ? nh : hunk;
|
||||||
@ -442,7 +442,7 @@ static int buffer_line_cb(
|
|||||||
} else {
|
} else {
|
||||||
/* Create a new buffer-blame hunk with this line */
|
/* Create a new buffer-blame hunk with this line */
|
||||||
shift_hunks_by(&blame->hunks, blame->current_diff_line, 1);
|
shift_hunks_by(&blame->hunks, blame->current_diff_line, 1);
|
||||||
blame->current_hunk = new_hunk((uint16_t)blame->current_diff_line, 1, 0, blame->path);
|
blame->current_hunk = new_hunk(blame->current_diff_line, 1, 0, blame->path);
|
||||||
GITERR_CHECK_ALLOC(blame->current_hunk);
|
GITERR_CHECK_ALLOC(blame->current_hunk);
|
||||||
|
|
||||||
git_vector_insert_sorted(&blame->hunks, blame->current_hunk, NULL);
|
git_vector_insert_sorted(&blame->hunks, blame->current_hunk, NULL);
|
||||||
|
@ -31,10 +31,10 @@ typedef struct git_blame__entry {
|
|||||||
/* the first line of this group in the final image;
|
/* the first line of this group in the final image;
|
||||||
* internally all line numbers are 0 based.
|
* internally all line numbers are 0 based.
|
||||||
*/
|
*/
|
||||||
int lno;
|
size_t lno;
|
||||||
|
|
||||||
/* how many lines this group has */
|
/* how many lines this group has */
|
||||||
int num_lines;
|
size_t num_lines;
|
||||||
|
|
||||||
/* the commit that introduced this group into the final image */
|
/* the commit that introduced this group into the final image */
|
||||||
git_blame__origin *suspect;
|
git_blame__origin *suspect;
|
||||||
@ -51,7 +51,7 @@ typedef struct git_blame__entry {
|
|||||||
/* the line number of the first line of this group in the
|
/* the line number of the first line of this group in the
|
||||||
* suspect's file; internally all line numbers are 0 based.
|
* suspect's file; internally all line numbers are 0 based.
|
||||||
*/
|
*/
|
||||||
int s_lno;
|
size_t s_lno;
|
||||||
|
|
||||||
/* how significant this entry is -- cached to avoid
|
/* how significant this entry is -- cached to avoid
|
||||||
* scanning the lines over and over.
|
* scanning the lines over and over.
|
||||||
|
@ -93,18 +93,25 @@ static bool same_suspect(git_blame__origin *a, git_blame__origin *b)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* find the line number of the last line the target is suspected for */
|
/* find the line number of the last line the target is suspected for */
|
||||||
static int find_last_in_target(git_blame *blame, git_blame__origin *target)
|
static bool find_last_in_target(size_t *out, git_blame *blame, git_blame__origin *target)
|
||||||
{
|
{
|
||||||
git_blame__entry *e;
|
git_blame__entry *e;
|
||||||
int last_in_target = -1;
|
size_t last_in_target = 0;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
*out = 0;
|
||||||
|
|
||||||
for (e=blame->ent; e; e=e->next) {
|
for (e=blame->ent; e; e=e->next) {
|
||||||
if (e->guilty || !same_suspect(e->suspect, target))
|
if (e->guilty || !same_suspect(e->suspect, target))
|
||||||
continue;
|
continue;
|
||||||
if (last_in_target < e->s_lno + e->num_lines)
|
if (last_in_target < e->s_lno + e->num_lines) {
|
||||||
|
found = true;
|
||||||
last_in_target = e->s_lno + e->num_lines;
|
last_in_target = e->s_lno + e->num_lines;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return last_in_target;
|
|
||||||
|
*out = last_in_target;
|
||||||
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -122,9 +129,9 @@ static int find_last_in_target(git_blame *blame, git_blame__origin *target)
|
|||||||
* to be blamed for the parent, and after that portion.
|
* to be blamed for the parent, and after that portion.
|
||||||
*/
|
*/
|
||||||
static void split_overlap(git_blame__entry *split, git_blame__entry *e,
|
static void split_overlap(git_blame__entry *split, git_blame__entry *e,
|
||||||
int tlno, int plno, int same, git_blame__origin *parent)
|
size_t tlno, size_t plno, size_t same, git_blame__origin *parent)
|
||||||
{
|
{
|
||||||
int chunk_end_lno;
|
size_t chunk_end_lno;
|
||||||
|
|
||||||
if (e->s_lno < tlno) {
|
if (e->s_lno < tlno) {
|
||||||
/* there is a pre-chunk part not blamed on the parent */
|
/* there is a pre-chunk part not blamed on the parent */
|
||||||
@ -265,9 +272,9 @@ static void decref_split(git_blame__entry *split)
|
|||||||
static void blame_overlap(
|
static void blame_overlap(
|
||||||
git_blame *blame,
|
git_blame *blame,
|
||||||
git_blame__entry *e,
|
git_blame__entry *e,
|
||||||
int tlno,
|
size_t tlno,
|
||||||
int plno,
|
size_t plno,
|
||||||
int same,
|
size_t same,
|
||||||
git_blame__origin *parent)
|
git_blame__origin *parent)
|
||||||
{
|
{
|
||||||
git_blame__entry split[3] = {{0}};
|
git_blame__entry split[3] = {{0}};
|
||||||
@ -285,9 +292,9 @@ static void blame_overlap(
|
|||||||
*/
|
*/
|
||||||
static void blame_chunk(
|
static void blame_chunk(
|
||||||
git_blame *blame,
|
git_blame *blame,
|
||||||
int tlno,
|
size_t tlno,
|
||||||
int plno,
|
size_t plno,
|
||||||
int same,
|
size_t same,
|
||||||
git_blame__origin *target,
|
git_blame__origin *target,
|
||||||
git_blame__origin *parent)
|
git_blame__origin *parent)
|
||||||
{
|
{
|
||||||
@ -314,7 +321,7 @@ static int my_emit(
|
|||||||
blame_chunk(d->blame, d->tlno, d->plno, start_b, d->target, d->parent);
|
blame_chunk(d->blame, d->tlno, d->plno, start_b, d->target, d->parent);
|
||||||
d->plno = start_a + count_a;
|
d->plno = start_a + count_a;
|
||||||
d->tlno = start_b + count_b;
|
d->tlno = start_b + count_b;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,12 +383,11 @@ static int pass_blame_to_parent(
|
|||||||
git_blame__origin *target,
|
git_blame__origin *target,
|
||||||
git_blame__origin *parent)
|
git_blame__origin *parent)
|
||||||
{
|
{
|
||||||
int last_in_target;
|
size_t last_in_target;
|
||||||
mmfile_t file_p, file_o;
|
mmfile_t file_p, file_o;
|
||||||
blame_chunk_cb_data d = { blame, target, parent, 0, 0 };
|
blame_chunk_cb_data d = { blame, target, parent, 0, 0 };
|
||||||
|
|
||||||
last_in_target = find_last_in_target(blame, target);
|
if (!find_last_in_target(&last_in_target, blame, target))
|
||||||
if (last_in_target < 0)
|
|
||||||
return 1; /* nothing remains for this target */
|
return 1; /* nothing remains for this target */
|
||||||
|
|
||||||
fill_origin_blob(parent, &file_p);
|
fill_origin_blob(parent, &file_p);
|
||||||
|
@ -4,7 +4,7 @@ void hunk_message(size_t idx, const git_blame_hunk *hunk, const char *fmt, ...)
|
|||||||
{
|
{
|
||||||
va_list arglist;
|
va_list arglist;
|
||||||
|
|
||||||
printf("Hunk %"PRIuZ" (line %d +%d): ", idx,
|
printf("Hunk %"PRIuZ" (line %"PRIuZ" +%"PRIuZ"): ", idx,
|
||||||
hunk->final_start_line_number, hunk->lines_in_hunk-1);
|
hunk->final_start_line_number, hunk->lines_in_hunk-1);
|
||||||
|
|
||||||
va_start(arglist, fmt);
|
va_start(arglist, fmt);
|
||||||
@ -15,7 +15,7 @@ void hunk_message(size_t idx, const git_blame_hunk *hunk, const char *fmt, ...)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void check_blame_hunk_index(git_repository *repo, git_blame *blame, int idx,
|
void check_blame_hunk_index(git_repository *repo, git_blame *blame, int idx,
|
||||||
int start_line, int len, char boundary, const char *commit_id, const char *orig_path)
|
size_t start_line, size_t len, char boundary, const char *commit_id, const char *orig_path)
|
||||||
{
|
{
|
||||||
char expected[GIT_OID_HEXSZ+1] = {0}, actual[GIT_OID_HEXSZ+1] = {0};
|
char expected[GIT_OID_HEXSZ+1] = {0}, actual[GIT_OID_HEXSZ+1] = {0};
|
||||||
const git_blame_hunk *hunk = git_blame_get_hunk_byindex(blame, idx);
|
const git_blame_hunk *hunk = git_blame_get_hunk_byindex(blame, idx);
|
||||||
|
@ -7,10 +7,8 @@ void check_blame_hunk_index(
|
|||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
git_blame *blame,
|
git_blame *blame,
|
||||||
int idx,
|
int idx,
|
||||||
int start_line,
|
size_t start_line,
|
||||||
int len,
|
size_t len,
|
||||||
char boundary,
|
char boundary,
|
||||||
const char *commit_id,
|
const char *commit_id,
|
||||||
const char *orig_path);
|
const char *orig_path);
|
||||||
|
|
||||||
|
|
||||||
|
@ -281,6 +281,18 @@ void test_blame_simple__can_restrict_lines_both(void)
|
|||||||
check_blame_hunk_index(g_repo, g_blame, 2, 6, 2, 0, "63d671eb", "b.txt");
|
check_blame_hunk_index(g_repo, g_blame, 2, 6, 2, 0, "63d671eb", "b.txt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_blame_simple__can_blame_huge_file(void)
|
||||||
|
{
|
||||||
|
git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
|
||||||
|
|
||||||
|
cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
|
||||||
|
|
||||||
|
cl_git_pass(git_blame_file(&g_blame, g_repo, "huge.txt", &opts));
|
||||||
|
cl_assert_equal_i(2, git_blame_get_hunk_count(g_blame));
|
||||||
|
check_blame_hunk_index(g_repo, g_blame, 0, 1, 65536, 0, "4eecfea", "huge.txt");
|
||||||
|
check_blame_hunk_index(g_repo, g_blame, 1, 65537, 1, 0, "6653ff4", "huge.txt");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* $ git blame -n branch_file.txt be3563a..HEAD
|
* $ git blame -n branch_file.txt be3563a..HEAD
|
||||||
* orig line no final line no
|
* orig line no final line no
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user