Skip to content

Commit 803bdfe

Browse files
author
Yao Qi
committed
Don't delete thread_info if refcount isn't zero
I build GDB with asan, and run test case hook-stop.exp, and threadapply.exp, I got the following asan error, =================================================================^M ^[[1m^[[31m==2291==ERROR: AddressSanitizer: heap-use-after-free on address 0x6160000999c4 at pc 0x000000826022 bp 0x7ffd28a8ff70 sp 0x7ffd28a8ff60^M ^[[1m^[[0m^[[1m^[[34mREAD of size 4 at 0x6160000999c4 thread T0^[[1m^[[0m^M #0 0x826021 in release_stop_context_cleanup ../../binutils-gdb/gdb/infrun.c:8203^M #1 0x72798a in do_my_cleanups ../../binutils-gdb/gdb/common/cleanups.c:154^M #2 0x727a32 in do_cleanups(cleanup*) ../../binutils-gdb/gdb/common/cleanups.c:176^M #3 0x826895 in normal_stop() ../../binutils-gdb/gdb/infrun.c:8381^M #4 0x815208 in fetch_inferior_event(void*) ../../binutils-gdb/gdb/infrun.c:4011^M #5 0x868aca in inferior_event_handler(inferior_event_type, void*) ../../binutils-gdb/gdb/inf-loop.c:44^M .... ^[[1m^[[32m0x6160000999c4 is located 68 bytes inside of 568-byte region [0x616000099980,0x616000099bb8)^M ^[[1m^[[0m^[[1m^[[35mfreed by thread T0 here:^[[1m^[[0m^M #0 0x7fb0bc1312ca in __interceptor_free (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x982ca)^M #1 0xb8c62f in xfree(void*) ../../binutils-gdb/gdb/common/common-utils.c:100^M #2 0x83df67 in free_thread ../../binutils-gdb/gdb/thread.c:207^M #3 0x83dfd2 in init_thread_list() ../../binutils-gdb/gdb/thread.c:223^M #4 0x805494 in kill_command ../../binutils-gdb/gdb/infcmd.c:2595^M .... Detaching from program: /home/yao.qi/SourceCode/gnu/build-with-asan/gdb/testsuite/outputs/gdb.threads/threadapply/threadapply, process 2399^M =================================================================^M ^[[1m^[[31m==2387==ERROR: AddressSanitizer: heap-use-after-free on address 0x6160000a98c0 at pc 0x00000083fd28 bp 0x7ffd401c3110 sp 0x7ffd401c3100^M ^[[1m^[[0m^[[1m^[[34mREAD of size 4 at 0x6160000a98c0 thread T0^[[1m^[[0m^M #0 0x83fd27 in thread_alive ../../binutils-gdb/gdb/thread.c:741^M #1 0x844277 in thread_apply_all_command ../../binutils-gdb/gdb/thread.c:1804^M .... ^M ^[[1m^[[32m0x6160000a98c0 is located 64 bytes inside of 568-byte region [0x6160000a9880,0x6160000a9ab8)^M ^[[1m^[[0m^[[1m^[[35mfreed by thread T0 here:^[[1m^[[0m^M #0 0x7f59a7e322ca in __interceptor_free (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x982ca)^M #1 0xb8c62f in xfree(void*) ../../binutils-gdb/gdb/common/common-utils.c:100^M #2 0x83df67 in free_thread ../../binutils-gdb/gdb/thread.c:207^M #3 0x83dfd2 in init_thread_list() ../../binutils-gdb/gdb/thread.c:223^M This patch fixes the issue by deleting thread_info object if it is deletable, otherwise, mark it as exited (by set_thread_exited). Function set_thread_exited is shared from delete_thread_1. This patch also moves field "refcount" to private and methods incref and decref. Additionally, we stop using "ptid_t" in "struct current_thread_cleanup" to reference threads, instead we use "thread_info" directly. Due to this change, we don't need restore_current_thread_ptid_changed anymore. gdb: 2017-04-10 Yao Qi <yao.qi@linaro.org> PR gdb/19942 * gdbthread.h (thread_info::deletable): New method. (thread_info::incref): New method. (thread_info::decref): New method. (thread_info::refcount): Move it to private. * infrun.c (save_stop_context): Call inc_refcount. (release_stop_context_cleanup): Likewise. * thread.c (set_thread_exited): New function. (init_thread_list): Delete "tp" only it is deletable, otherwise call set_thread_exited. (delete_thread_1): Call set_thread_exited. (current_thread_cleanup) <inferior_pid>: Remove. <thread>: New field. (restore_current_thread_ptid_changed): Removed. (do_restore_current_thread_cleanup): Adjust. (restore_current_thread_cleanup_dtor): Don't call find_thread_ptid. (set_thread_refcount): Use dec_refcount. (make_cleanup_restore_current_thread): Adjust. (thread_apply_all_command): Call inc_refcount. (_initialize_thread): Don't call observer_attach_thread_ptid_changed.
1 parent 8c25b49 commit 803bdfe

File tree

4 files changed

+101
-62
lines changed

4 files changed

+101
-62
lines changed

gdb/ChangeLog

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,28 @@
1+
2017-04-10 Yao Qi <yao.qi@linaro.org>
2+
3+
PR gdb/19942
4+
* gdbthread.h (thread_info::deletable): New method.
5+
(thread_info::incref): New method.
6+
(thread_info::decref): New method.
7+
(thread_info::refcount): Move it to private.
8+
* infrun.c (save_stop_context): Call inc_refcount.
9+
(release_stop_context_cleanup): Likewise.
10+
* thread.c (set_thread_exited): New function.
11+
(init_thread_list): Delete "tp" only it is deletable, otherwise
12+
call set_thread_exited.
13+
(delete_thread_1): Call set_thread_exited.
14+
(current_thread_cleanup) <inferior_pid>: Remove.
15+
<thread>: New field.
16+
(restore_current_thread_ptid_changed): Removed.
17+
(do_restore_current_thread_cleanup): Adjust.
18+
(restore_current_thread_cleanup_dtor): Don't call
19+
find_thread_ptid.
20+
(set_thread_refcount): Use dec_refcount.
21+
(make_cleanup_restore_current_thread): Adjust.
22+
(thread_apply_all_command): Call inc_refcount.
23+
(_initialize_thread): Don't call
24+
observer_attach_thread_ptid_changed.
25+
126
2017-04-10 Yao Qi <yao.qi@linaro.org>
227

328
* thread.c (delete_thread_1): Hoist code on marking thread as

gdb/gdbthread.h

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,27 @@ struct thread_info
183183
explicit thread_info (inferior *inf, ptid_t ptid);
184184
~thread_info ();
185185

186+
bool deletable () const
187+
{
188+
/* If this is the current thread, or there's code out there that
189+
relies on it existing (m_refcount > 0) we can't delete yet. */
190+
return (m_refcount == 0 && !ptid_equal (ptid, inferior_ptid));
191+
}
192+
193+
/* Increase the refcount. */
194+
void incref ()
195+
{
196+
gdb_assert (m_refcount >= 0);
197+
m_refcount++;
198+
}
199+
200+
/* Decrease the refcount. */
201+
void decref ()
202+
{
203+
m_refcount--;
204+
gdb_assert (m_refcount >= 0);
205+
}
206+
186207
struct thread_info *next = NULL;
187208
ptid_t ptid; /* "Actual process id";
188209
In fact, this may be overloaded with
@@ -254,11 +275,6 @@ struct thread_info
254275
but STATE will still be THREAD_RUNNING. */
255276
enum thread_state state = THREAD_STOPPED;
256277

257-
/* If this is > 0, then it means there's code out there that relies
258-
on this thread being listed. Don't delete it from the lists even
259-
if we detect it exiting. */
260-
int refcount = 0;
261-
262278
/* State of GDB control of inferior thread execution.
263279
See `struct thread_control_state'. */
264280
thread_control_state control {};
@@ -346,6 +362,13 @@ struct thread_info
346362
fields point to self. */
347363
struct thread_info *step_over_prev = NULL;
348364
struct thread_info *step_over_next = NULL;
365+
366+
private:
367+
368+
/* If this is > 0, then it means there's code out there that relies
369+
on this thread being listed. Don't delete it from the lists even
370+
if we detect it exiting. */
371+
int m_refcount = 0;
349372
};
350373

351374
/* Create an empty thread list, or empty the existing one. */

gdb/infrun.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8165,7 +8165,7 @@ save_stop_context (void)
81658165
/* Take a strong reference so that the thread can't be deleted
81668166
yet. */
81678167
sc->thread = inferior_thread ();
8168-
sc->thread->refcount++;
8168+
sc->thread->incref ();
81698169
}
81708170
else
81718171
sc->thread = NULL;
@@ -8182,7 +8182,7 @@ release_stop_context_cleanup (void *arg)
81828182
struct stop_context *sc = (struct stop_context *) arg;
81838183

81848184
if (sc->thread != NULL)
8185-
sc->thread->refcount--;
8185+
sc->thread->decref ();
81868186
xfree (sc);
81878187
}
81888188

gdb/thread.c

Lines changed: 46 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,27 @@ clear_thread_inferior_resources (struct thread_info *tp)
192192
thread_cancel_execution_command (tp);
193193
}
194194

195+
/* Set the TP's state as exited. */
196+
197+
static void
198+
set_thread_exited (thread_info *tp, int silent)
199+
{
200+
/* Dead threads don't need to step-over. Remove from queue. */
201+
if (tp->step_over_next != NULL)
202+
thread_step_over_chain_remove (tp);
203+
204+
if (tp->state != THREAD_EXITED)
205+
{
206+
observer_notify_thread_exit (tp, silent);
207+
208+
/* Tag it as exited. */
209+
tp->state = THREAD_EXITED;
210+
211+
/* Clear breakpoints, etc. associated with this thread. */
212+
clear_thread_inferior_resources (tp);
213+
}
214+
}
215+
195216
void
196217
init_thread_list (void)
197218
{
@@ -205,7 +226,10 @@ init_thread_list (void)
205226
for (tp = thread_list; tp; tp = tpnext)
206227
{
207228
tpnext = tp->next;
208-
delete tp;
229+
if (tp->deletable ())
230+
delete tp;
231+
else
232+
set_thread_exited (tp, 1);
209233
}
210234

211235
thread_list = NULL;
@@ -430,25 +454,9 @@ delete_thread_1 (ptid_t ptid, int silent)
430454
if (!tp)
431455
return;
432456

433-
/* Dead threads don't need to step-over. Remove from queue. */
434-
if (tp->step_over_next != NULL)
435-
thread_step_over_chain_remove (tp);
457+
set_thread_exited (tp, silent);
436458

437-
if (tp->state != THREAD_EXITED)
438-
{
439-
observer_notify_thread_exit (tp, silent);
440-
441-
/* Tag it as exited. */
442-
tp->state = THREAD_EXITED;
443-
444-
/* Clear breakpoints, etc. associated with this thread. */
445-
clear_thread_inferior_resources (tp);
446-
}
447-
448-
/* If this is the current thread, or there's code out there that
449-
relies on it existing (refcount > 0) we can't delete yet. */
450-
if (tp->refcount > 0
451-
|| ptid_equal (tp->ptid, inferior_ptid))
459+
if (!tp->deletable ())
452460
{
453461
/* Will be really deleted some other time. */
454462
return;
@@ -1546,7 +1554,7 @@ struct current_thread_cleanup
15461554
'current_thread_cleanup_chain' below. */
15471555
struct current_thread_cleanup *next;
15481556

1549-
ptid_t inferior_ptid;
1557+
thread_info *thread;
15501558
struct frame_id selected_frame_id;
15511559
int selected_frame_level;
15521560
int was_stopped;
@@ -1561,37 +1569,21 @@ struct current_thread_cleanup
15611569
succeeds. */
15621570
static struct current_thread_cleanup *current_thread_cleanup_chain;
15631571

1564-
/* A thread_ptid_changed observer. Update all currently installed
1565-
current_thread_cleanup cleanups that want to switch back to
1566-
OLD_PTID to switch back to NEW_PTID instead. */
1567-
1568-
static void
1569-
restore_current_thread_ptid_changed (ptid_t old_ptid, ptid_t new_ptid)
1570-
{
1571-
struct current_thread_cleanup *it;
1572-
1573-
for (it = current_thread_cleanup_chain; it != NULL; it = it->next)
1574-
{
1575-
if (ptid_equal (it->inferior_ptid, old_ptid))
1576-
it->inferior_ptid = new_ptid;
1577-
}
1578-
}
1579-
15801572
static void
15811573
do_restore_current_thread_cleanup (void *arg)
15821574
{
1583-
struct thread_info *tp;
15841575
struct current_thread_cleanup *old = (struct current_thread_cleanup *) arg;
15851576

1586-
tp = find_thread_ptid (old->inferior_ptid);
1587-
1588-
/* If the previously selected thread belonged to a process that has
1589-
in the mean time been deleted (due to normal exit, detach, etc.),
1590-
then don't revert back to it, but instead simply drop back to no
1591-
thread selected. */
1592-
if (tp
1593-
&& find_inferior_ptid (tp->ptid) != NULL)
1594-
restore_current_thread (old->inferior_ptid);
1577+
/* If an entry of thread_info was previously selected, it won't be
1578+
deleted because we've increased its refcount. The thread represented
1579+
by this thread_info entry may have already exited (due to normal exit,
1580+
detach, etc), so the thread_info.state is THREAD_EXITED. */
1581+
if (old->thread != NULL
1582+
/* If the previously selected thread belonged to a process that has
1583+
in the mean time exited (or killed, detached, etc.), then don't revert
1584+
back to it, but instead simply drop back to no thread selected. */
1585+
&& find_inferior_ptid (old->thread->ptid) != NULL)
1586+
restore_current_thread (old->thread->ptid);
15951587
else
15961588
{
15971589
restore_current_thread (null_ptid);
@@ -1619,9 +1611,9 @@ restore_current_thread_cleanup_dtor (void *arg)
16191611

16201612
current_thread_cleanup_chain = current_thread_cleanup_chain->next;
16211613

1622-
tp = find_thread_ptid (old->inferior_ptid);
1623-
if (tp)
1624-
tp->refcount--;
1614+
if (old->thread != NULL)
1615+
old->thread->decref ();
1616+
16251617
inf = find_inferior_id (old->inf_id);
16261618
if (inf != NULL)
16271619
inf->removable = old->was_removable;
@@ -1638,15 +1630,15 @@ set_thread_refcount (void *data)
16381630
= (struct thread_array_cleanup *) data;
16391631

16401632
for (k = 0; k != ta_cleanup->count; k++)
1641-
ta_cleanup->tp_array[k]->refcount--;
1633+
ta_cleanup->tp_array[k]->decref ();
16421634
}
16431635

16441636
struct cleanup *
16451637
make_cleanup_restore_current_thread (void)
16461638
{
16471639
struct current_thread_cleanup *old = XNEW (struct current_thread_cleanup);
16481640

1649-
old->inferior_ptid = inferior_ptid;
1641+
old->thread = NULL;
16501642
old->inf_id = current_inferior ()->num;
16511643
old->was_removable = current_inferior ()->removable;
16521644

@@ -1679,7 +1671,8 @@ make_cleanup_restore_current_thread (void)
16791671
struct thread_info *tp = find_thread_ptid (inferior_ptid);
16801672

16811673
if (tp)
1682-
tp->refcount++;
1674+
tp->incref ();
1675+
old->thread = tp;
16831676
}
16841677

16851678
current_inferior ()->removable = 0;
@@ -1796,7 +1789,7 @@ thread_apply_all_command (char *cmd, int from_tty)
17961789
ALL_NON_EXITED_THREADS (tp)
17971790
{
17981791
tp_array[i] = tp;
1799-
tp->refcount++;
1792+
tp->incref ();
18001793
i++;
18011794
}
18021795
/* Because we skipped exited threads, we may end up with fewer
@@ -2286,6 +2279,4 @@ Show printing of thread events (such as thread start and exit)."), NULL,
22862279

22872280
create_internalvar_type_lazy ("_thread", &thread_funcs, NULL);
22882281
create_internalvar_type_lazy ("_gthread", &gthread_funcs, NULL);
2289-
2290-
observer_attach_thread_ptid_changed (restore_current_thread_ptid_changed);
22912282
}

0 commit comments

Comments
 (0)