Skip to content

Commit 430dc8b

Browse files
libbacktrace: avoid ambiguous binary search
Searching for a range match can cause the search order to not match the sort order, which can cause libbacktrace to miss matching entries. Allocate an extra entry at the end of function_addrs and unit_addrs vectors, so that we can safely compare to the next entry when searching. Adjust the matching code accordingly. Fixes #44 * dwarf.c (function_addrs_search): Compare against the next entry low address, not the high address. (unit_addrs_search): Likewise. (build_address_map): Add a trailing unit_addrs. (read_function_entry): Add a trailing function_addrs. (read_function_info): Likewise. (report_inlined_functions): Search backward for function_addrs match. (dwarf_lookup_pc): Search backward for unit_addrs and function_addrs matches.
1 parent 5dec0fa commit 430dc8b

File tree

1 file changed

+135
-45
lines changed

1 file changed

+135
-45
lines changed

dwarf.c

Lines changed: 135 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,9 +1497,11 @@ function_addrs_compare (const void *v1, const void *v2)
14971497
return strcmp (a1->function->name, a2->function->name);
14981498
}
14991499

1500-
/* Compare a PC against a function_addrs for bsearch. Note that if
1501-
there are multiple ranges containing PC, which one will be returned
1502-
is unpredictable. We compensate for that in dwarf_fileline. */
1500+
/* Compare a PC against a function_addrs for bsearch. We always
1501+
allocate an entra entry at the end of the vector, so that this
1502+
routine can safely look at the next entry. Note that if there are
1503+
multiple ranges containing PC, which one will be returned is
1504+
unpredictable. We compensate for that in dwarf_fileline. */
15031505

15041506
static int
15051507
function_addrs_search (const void *vkey, const void *ventry)
@@ -1511,7 +1513,7 @@ function_addrs_search (const void *vkey, const void *ventry)
15111513
pc = *key;
15121514
if (pc < entry->low)
15131515
return -1;
1514-
else if (pc >= entry->high)
1516+
else if (pc > (entry + 1)->low)
15151517
return 1;
15161518
else
15171519
return 0;
@@ -1582,9 +1584,11 @@ unit_addrs_compare (const void *v1, const void *v2)
15821584
return 0;
15831585
}
15841586

1585-
/* Compare a PC against a unit_addrs for bsearch. Note that if there
1586-
are multiple ranges containing PC, which one will be returned is
1587-
unpredictable. We compensate for that in dwarf_fileline. */
1587+
/* Compare a PC against a unit_addrs for bsearch. We always allocate
1588+
an entry entry at the end of the vector, so that this routine can
1589+
safely look at the next entry. Note that if there are multiple
1590+
ranges containing PC, which one will be returned is unpredictable.
1591+
We compensate for that in dwarf_fileline. */
15881592

15891593
static int
15901594
unit_addrs_search (const void *vkey, const void *ventry)
@@ -1596,7 +1600,7 @@ unit_addrs_search (const void *vkey, const void *ventry)
15961600
pc = *key;
15971601
if (pc < entry->low)
15981602
return -1;
1599-
else if (pc >= entry->high)
1603+
else if (pc > (entry + 1)->low)
16001604
return 1;
16011605
else
16021606
return 0;
@@ -2424,6 +2428,7 @@ build_address_map (struct backtrace_state *state, uintptr_t base_address,
24242428
size_t i;
24252429
struct unit **pu;
24262430
size_t unit_offset = 0;
2431+
struct unit_addrs *pa;
24272432

24282433
memset (&addrs->vec, 0, sizeof addrs->vec);
24292434
memset (&unit_vec->vec, 0, sizeof unit_vec->vec);
@@ -2564,6 +2569,17 @@ build_address_map (struct backtrace_state *state, uintptr_t base_address,
25642569
if (info.reported_underflow)
25652570
goto fail;
25662571

2572+
/* Add a trailing addrs entry, but don't include it in addrs->count. */
2573+
pa = ((struct unit_addrs *)
2574+
backtrace_vector_grow (state, sizeof (struct unit_addrs),
2575+
error_callback, data, &addrs->vec));
2576+
if (pa == NULL)
2577+
goto fail;
2578+
pa->low = 0;
2579+
--pa->low;
2580+
pa->high = pa->low;
2581+
pa->u = NULL;
2582+
25672583
unit_vec->vec = units;
25682584
unit_vec->count = units_count;
25692585
return 1;
@@ -3737,8 +3753,23 @@ read_function_entry (struct backtrace_state *state, struct dwarf_data *ddata,
37373753

37383754
if (fvec.count > 0)
37393755
{
3756+
struct function_addrs *p;
37403757
struct function_addrs *faddrs;
37413758

3759+
/* Allocate a trailing entry, but don't include it
3760+
in fvec.count. */
3761+
p = ((struct function_addrs *)
3762+
backtrace_vector_grow (state,
3763+
sizeof (struct function_addrs),
3764+
error_callback, data,
3765+
&fvec.vec));
3766+
if (p == NULL)
3767+
return 0;
3768+
p->low = 0;
3769+
--p->low;
3770+
p->high = p->low;
3771+
p->function = NULL;
3772+
37423773
if (!backtrace_vector_release (state, &fvec.vec,
37433774
error_callback, data))
37443775
return 0;
@@ -3772,6 +3803,7 @@ read_function_info (struct backtrace_state *state, struct dwarf_data *ddata,
37723803
struct function_vector lvec;
37733804
struct function_vector *pfvec;
37743805
struct dwarf_buf unit_buf;
3806+
struct function_addrs *p;
37753807
struct function_addrs *addrs;
37763808
size_t addrs_count;
37773809

@@ -3803,6 +3835,18 @@ read_function_info (struct backtrace_state *state, struct dwarf_data *ddata,
38033835
if (pfvec->count == 0)
38043836
return;
38053837

3838+
/* Allocate a trailing entry, but don't include it in
3839+
pfvec->count. */
3840+
p = ((struct function_addrs *)
3841+
backtrace_vector_grow (state, sizeof (struct function_addrs),
3842+
error_callback, data, &pfvec->vec));
3843+
if (p == NULL)
3844+
return;
3845+
p->low = 0;
3846+
--p->low;
3847+
p->high = p->low;
3848+
p->function = NULL;
3849+
38063850
addrs_count = pfvec->count;
38073851

38083852
if (fvec == NULL)
@@ -3839,30 +3883,46 @@ report_inlined_functions (uintptr_t pc, struct function *function,
38393883
backtrace_full_callback callback, void *data,
38403884
const char **filename, int *lineno)
38413885
{
3842-
struct function_addrs *function_addrs;
3886+
struct function_addrs *p;
3887+
struct function_addrs *match;
38433888
struct function *inlined;
38443889
int ret;
38453890

38463891
if (function->function_addrs_count == 0)
38473892
return 0;
38483893

3849-
function_addrs = ((struct function_addrs *)
3850-
bsearch (&pc, function->function_addrs,
3851-
function->function_addrs_count,
3852-
sizeof (struct function_addrs),
3853-
function_addrs_search));
3854-
if (function_addrs == NULL)
3894+
p = ((struct function_addrs *)
3895+
bsearch (&pc, function->function_addrs,
3896+
function->function_addrs_count,
3897+
sizeof (struct function_addrs),
3898+
function_addrs_search));
3899+
if (p == NULL)
38553900
return 0;
38563901

3857-
while (((size_t) (function_addrs - function->function_addrs) + 1
3858-
< function->function_addrs_count)
3859-
&& pc >= (function_addrs + 1)->low
3860-
&& pc < (function_addrs + 1)->high)
3861-
++function_addrs;
3902+
/* Here pc >= p->low && pc < (p + 1)->low. The function_addrs are
3903+
sorted by low, so we are at the end of a range of function_addrs
3904+
with the same low alue. Walk backward and use the first range
3905+
that includes pc. */
3906+
match = NULL;
3907+
while (1)
3908+
{
3909+
if (pc < p->high)
3910+
{
3911+
match = p;
3912+
break;
3913+
}
3914+
if (p == function->function_addrs)
3915+
break;
3916+
if ((p - 1)->low < p->low)
3917+
break;
3918+
--p;
3919+
}
3920+
if (match == NULL)
3921+
return 0;
38623922

38633923
/* We found an inlined call. */
38643924

3865-
inlined = function_addrs->function;
3925+
inlined = match->function;
38663926

38673927
/* Report any calls inlined into this one. */
38683928
ret = report_inlined_functions (pc, inlined, callback, data,
@@ -3895,11 +3955,13 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata,
38953955
int *found)
38963956
{
38973957
struct unit_addrs *entry;
3958+
int found_entry;
38983959
struct unit *u;
38993960
int new_data;
39003961
struct line *lines;
39013962
struct line *ln;
3902-
struct function_addrs *function_addrs;
3963+
struct function_addrs *p;
3964+
struct function_addrs *fmatch;
39033965
struct function *function;
39043966
const char *filename;
39053967
int lineno;
@@ -3919,14 +3981,29 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata,
39193981
return 0;
39203982
}
39213983

3922-
/* If there are multiple ranges that contain PC, use the last one,
3923-
in order to produce predictable results. If we assume that all
3924-
ranges are properly nested, then the last range will be the
3925-
smallest one. */
3926-
while ((size_t) (entry - ddata->addrs) + 1 < ddata->addrs_count
3927-
&& pc >= (entry + 1)->low
3928-
&& pc < (entry + 1)->high)
3929-
++entry;
3984+
/* Here pc >= entry->low && pc < (entry + 1)->low. The unit_addrs
3985+
are sorted by low, so we are at the end of a range of unit_addrs
3986+
with the same low value. Walk backward and use the first range
3987+
that includes pc. */
3988+
found_entry = 0;
3989+
while (1)
3990+
{
3991+
if (pc < entry->high)
3992+
{
3993+
found_entry = 1;
3994+
break;
3995+
}
3996+
if (entry == ddata->addrs)
3997+
break;
3998+
if ((entry - 1)->low < entry->low)
3999+
break;
4000+
--entry;
4001+
}
4002+
if (!found_entry)
4003+
{
4004+
*found = 0;
4005+
return 0;
4006+
}
39304007

39314008
/* We need the lines, lines_count, function_addrs,
39324009
function_addrs_count fields of u. If they are not set, we need
@@ -3962,6 +4039,7 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata,
39624039
new_data = 0;
39634040
if (lines == NULL)
39644041
{
4042+
struct function_addrs *function_addrs;
39654043
size_t function_addrs_count;
39664044
struct line_header lhdr;
39674045
size_t count;
@@ -4078,24 +4156,36 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata,
40784156
if (entry->u->function_addrs_count == 0)
40794157
return callback (data, pc, ln->filename, ln->lineno, NULL);
40804158

4081-
function_addrs = ((struct function_addrs *)
4082-
bsearch (&pc, entry->u->function_addrs,
4083-
entry->u->function_addrs_count,
4084-
sizeof (struct function_addrs),
4085-
function_addrs_search));
4086-
if (function_addrs == NULL)
4159+
p = ((struct function_addrs *)
4160+
bsearch (&pc, entry->u->function_addrs,
4161+
entry->u->function_addrs_count,
4162+
sizeof (struct function_addrs),
4163+
function_addrs_search));
4164+
if (p == NULL)
40874165
return callback (data, pc, ln->filename, ln->lineno, NULL);
40884166

4089-
/* If there are multiple function ranges that contain PC, use the
4090-
last one, in order to produce predictable results. */
4091-
4092-
while (((size_t) (function_addrs - entry->u->function_addrs + 1)
4093-
< entry->u->function_addrs_count)
4094-
&& pc >= (function_addrs + 1)->low
4095-
&& pc < (function_addrs + 1)->high)
4096-
++function_addrs;
4167+
/* Here pc >= p->low && pc < (p + 1)->low. The function_addrs are
4168+
sorted by low, so we are at the end of a range of function_addrs
4169+
with the same low alue. Walk backward and use the first range
4170+
that includes pc. */
4171+
fmatch = NULL;
4172+
while (1)
4173+
{
4174+
if (pc < p->high)
4175+
{
4176+
fmatch = p;
4177+
break;
4178+
}
4179+
if (p == entry->u->function_addrs)
4180+
break;
4181+
if ((p - 1)->low < p->low)
4182+
break;
4183+
--p;
4184+
}
4185+
if (fmatch == NULL)
4186+
return callback (data, pc, ln->filename, ln->lineno, NULL);
40974187

4098-
function = function_addrs->function;
4188+
function = fmatch->function;
40994189

41004190
filename = ln->filename;
41014191
lineno = ln->lineno;

0 commit comments

Comments
 (0)