Skip to content

Commit 8d46003

Browse files
author
KJ Tsanaktsidis
committed
Mark asan fake stacks during machine stack marking
ASAN leaves a pointer to the fake frame on the stack; we can use the __asan_addr_is_in_fake_stack API to work out the extent of the fake stack and thus mark any VALUEs contained therein. [Bug #20001]
1 parent 5b3aadb commit 8d46003

File tree

8 files changed

+162
-3
lines changed

8 files changed

+162
-3
lines changed

common.mk

Lines changed: 43 additions & 0 deletions
Large diffs are not rendered by default.

ext/objspace/depend

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ object_tracing.o: $(top_srcdir)/internal/basic_operators.h
182182
object_tracing.o: $(top_srcdir)/internal/compilers.h
183183
object_tracing.o: $(top_srcdir)/internal/gc.h
184184
object_tracing.o: $(top_srcdir)/internal/imemo.h
185+
object_tracing.o: $(top_srcdir)/internal/sanitizers.h
185186
object_tracing.o: $(top_srcdir)/internal/serial.h
186187
object_tracing.o: $(top_srcdir)/internal/static_assert.h
187188
object_tracing.o: $(top_srcdir)/internal/vm.h

ext/ripper/depend

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,7 @@ ripper.o: $(top_srcdir)/internal/parse.h
593593
ripper.o: $(top_srcdir)/internal/rational.h
594594
ripper.o: $(top_srcdir)/internal/re.h
595595
ripper.o: $(top_srcdir)/internal/ruby_parser.h
596+
ripper.o: $(top_srcdir)/internal/sanitizers.h
596597
ripper.o: $(top_srcdir)/internal/serial.h
597598
ripper.o: $(top_srcdir)/internal/static_assert.h
598599
ripper.o: $(top_srcdir)/internal/string.h

ext/socket/depend

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ ancdata.o: $(top_srcdir)/internal/error.h
199199
ancdata.o: $(top_srcdir)/internal/gc.h
200200
ancdata.o: $(top_srcdir)/internal/imemo.h
201201
ancdata.o: $(top_srcdir)/internal/io.h
202+
ancdata.o: $(top_srcdir)/internal/sanitizers.h
202203
ancdata.o: $(top_srcdir)/internal/serial.h
203204
ancdata.o: $(top_srcdir)/internal/static_assert.h
204205
ancdata.o: $(top_srcdir)/internal/string.h
@@ -408,6 +409,7 @@ basicsocket.o: $(top_srcdir)/internal/error.h
408409
basicsocket.o: $(top_srcdir)/internal/gc.h
409410
basicsocket.o: $(top_srcdir)/internal/imemo.h
410411
basicsocket.o: $(top_srcdir)/internal/io.h
412+
basicsocket.o: $(top_srcdir)/internal/sanitizers.h
411413
basicsocket.o: $(top_srcdir)/internal/serial.h
412414
basicsocket.o: $(top_srcdir)/internal/static_assert.h
413415
basicsocket.o: $(top_srcdir)/internal/string.h
@@ -617,6 +619,7 @@ constants.o: $(top_srcdir)/internal/error.h
617619
constants.o: $(top_srcdir)/internal/gc.h
618620
constants.o: $(top_srcdir)/internal/imemo.h
619621
constants.o: $(top_srcdir)/internal/io.h
622+
constants.o: $(top_srcdir)/internal/sanitizers.h
620623
constants.o: $(top_srcdir)/internal/serial.h
621624
constants.o: $(top_srcdir)/internal/static_assert.h
622625
constants.o: $(top_srcdir)/internal/string.h
@@ -827,6 +830,7 @@ ifaddr.o: $(top_srcdir)/internal/error.h
827830
ifaddr.o: $(top_srcdir)/internal/gc.h
828831
ifaddr.o: $(top_srcdir)/internal/imemo.h
829832
ifaddr.o: $(top_srcdir)/internal/io.h
833+
ifaddr.o: $(top_srcdir)/internal/sanitizers.h
830834
ifaddr.o: $(top_srcdir)/internal/serial.h
831835
ifaddr.o: $(top_srcdir)/internal/static_assert.h
832836
ifaddr.o: $(top_srcdir)/internal/string.h
@@ -1036,6 +1040,7 @@ init.o: $(top_srcdir)/internal/error.h
10361040
init.o: $(top_srcdir)/internal/gc.h
10371041
init.o: $(top_srcdir)/internal/imemo.h
10381042
init.o: $(top_srcdir)/internal/io.h
1043+
init.o: $(top_srcdir)/internal/sanitizers.h
10391044
init.o: $(top_srcdir)/internal/serial.h
10401045
init.o: $(top_srcdir)/internal/static_assert.h
10411046
init.o: $(top_srcdir)/internal/string.h
@@ -1245,6 +1250,7 @@ ipsocket.o: $(top_srcdir)/internal/error.h
12451250
ipsocket.o: $(top_srcdir)/internal/gc.h
12461251
ipsocket.o: $(top_srcdir)/internal/imemo.h
12471252
ipsocket.o: $(top_srcdir)/internal/io.h
1253+
ipsocket.o: $(top_srcdir)/internal/sanitizers.h
12481254
ipsocket.o: $(top_srcdir)/internal/serial.h
12491255
ipsocket.o: $(top_srcdir)/internal/static_assert.h
12501256
ipsocket.o: $(top_srcdir)/internal/string.h
@@ -1454,6 +1460,7 @@ option.o: $(top_srcdir)/internal/error.h
14541460
option.o: $(top_srcdir)/internal/gc.h
14551461
option.o: $(top_srcdir)/internal/imemo.h
14561462
option.o: $(top_srcdir)/internal/io.h
1463+
option.o: $(top_srcdir)/internal/sanitizers.h
14571464
option.o: $(top_srcdir)/internal/serial.h
14581465
option.o: $(top_srcdir)/internal/static_assert.h
14591466
option.o: $(top_srcdir)/internal/string.h
@@ -1663,6 +1670,7 @@ raddrinfo.o: $(top_srcdir)/internal/error.h
16631670
raddrinfo.o: $(top_srcdir)/internal/gc.h
16641671
raddrinfo.o: $(top_srcdir)/internal/imemo.h
16651672
raddrinfo.o: $(top_srcdir)/internal/io.h
1673+
raddrinfo.o: $(top_srcdir)/internal/sanitizers.h
16661674
raddrinfo.o: $(top_srcdir)/internal/serial.h
16671675
raddrinfo.o: $(top_srcdir)/internal/static_assert.h
16681676
raddrinfo.o: $(top_srcdir)/internal/string.h
@@ -1872,6 +1880,7 @@ socket.o: $(top_srcdir)/internal/error.h
18721880
socket.o: $(top_srcdir)/internal/gc.h
18731881
socket.o: $(top_srcdir)/internal/imemo.h
18741882
socket.o: $(top_srcdir)/internal/io.h
1883+
socket.o: $(top_srcdir)/internal/sanitizers.h
18751884
socket.o: $(top_srcdir)/internal/serial.h
18761885
socket.o: $(top_srcdir)/internal/static_assert.h
18771886
socket.o: $(top_srcdir)/internal/string.h
@@ -2081,6 +2090,7 @@ sockssocket.o: $(top_srcdir)/internal/error.h
20812090
sockssocket.o: $(top_srcdir)/internal/gc.h
20822091
sockssocket.o: $(top_srcdir)/internal/imemo.h
20832092
sockssocket.o: $(top_srcdir)/internal/io.h
2093+
sockssocket.o: $(top_srcdir)/internal/sanitizers.h
20842094
sockssocket.o: $(top_srcdir)/internal/serial.h
20852095
sockssocket.o: $(top_srcdir)/internal/static_assert.h
20862096
sockssocket.o: $(top_srcdir)/internal/string.h
@@ -2290,6 +2300,7 @@ tcpserver.o: $(top_srcdir)/internal/error.h
22902300
tcpserver.o: $(top_srcdir)/internal/gc.h
22912301
tcpserver.o: $(top_srcdir)/internal/imemo.h
22922302
tcpserver.o: $(top_srcdir)/internal/io.h
2303+
tcpserver.o: $(top_srcdir)/internal/sanitizers.h
22932304
tcpserver.o: $(top_srcdir)/internal/serial.h
22942305
tcpserver.o: $(top_srcdir)/internal/static_assert.h
22952306
tcpserver.o: $(top_srcdir)/internal/string.h
@@ -2499,6 +2510,7 @@ tcpsocket.o: $(top_srcdir)/internal/error.h
24992510
tcpsocket.o: $(top_srcdir)/internal/gc.h
25002511
tcpsocket.o: $(top_srcdir)/internal/imemo.h
25012512
tcpsocket.o: $(top_srcdir)/internal/io.h
2513+
tcpsocket.o: $(top_srcdir)/internal/sanitizers.h
25022514
tcpsocket.o: $(top_srcdir)/internal/serial.h
25032515
tcpsocket.o: $(top_srcdir)/internal/static_assert.h
25042516
tcpsocket.o: $(top_srcdir)/internal/string.h
@@ -2708,6 +2720,7 @@ udpsocket.o: $(top_srcdir)/internal/error.h
27082720
udpsocket.o: $(top_srcdir)/internal/gc.h
27092721
udpsocket.o: $(top_srcdir)/internal/imemo.h
27102722
udpsocket.o: $(top_srcdir)/internal/io.h
2723+
udpsocket.o: $(top_srcdir)/internal/sanitizers.h
27112724
udpsocket.o: $(top_srcdir)/internal/serial.h
27122725
udpsocket.o: $(top_srcdir)/internal/static_assert.h
27132726
udpsocket.o: $(top_srcdir)/internal/string.h
@@ -2917,6 +2930,7 @@ unixserver.o: $(top_srcdir)/internal/error.h
29172930
unixserver.o: $(top_srcdir)/internal/gc.h
29182931
unixserver.o: $(top_srcdir)/internal/imemo.h
29192932
unixserver.o: $(top_srcdir)/internal/io.h
2933+
unixserver.o: $(top_srcdir)/internal/sanitizers.h
29202934
unixserver.o: $(top_srcdir)/internal/serial.h
29212935
unixserver.o: $(top_srcdir)/internal/static_assert.h
29222936
unixserver.o: $(top_srcdir)/internal/string.h
@@ -3126,6 +3140,7 @@ unixsocket.o: $(top_srcdir)/internal/error.h
31263140
unixsocket.o: $(top_srcdir)/internal/gc.h
31273141
unixsocket.o: $(top_srcdir)/internal/imemo.h
31283142
unixsocket.o: $(top_srcdir)/internal/io.h
3143+
unixsocket.o: $(top_srcdir)/internal/sanitizers.h
31293144
unixsocket.o: $(top_srcdir)/internal/serial.h
31303145
unixsocket.o: $(top_srcdir)/internal/static_assert.h
31313146
unixsocket.o: $(top_srcdir)/internal/string.h

gc.c

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -953,6 +953,11 @@ typedef struct rb_objspace {
953953

954954
rb_darray(VALUE *) weak_references;
955955
rb_postponed_job_handle_t finalize_deferred_pjob;
956+
957+
#ifdef RUBY_ASAN_ENABLED
958+
rb_execution_context_t *marking_machine_context_ec;
959+
#endif
960+
956961
} rb_objspace_t;
957962

958963

@@ -6821,6 +6826,26 @@ mark_const_tbl(rb_objspace_t *objspace, struct rb_id_table *tbl)
68216826
static void each_stack_location(rb_objspace_t *objspace, const rb_execution_context_t *ec,
68226827
const VALUE *stack_start, const VALUE *stack_end, void (*cb)(rb_objspace_t *, VALUE));
68236828

6829+
static void
6830+
gc_mark_machine_stack_location_maybe(rb_objspace_t *objspace, VALUE obj)
6831+
{
6832+
gc_mark_maybe(objspace, obj);
6833+
6834+
#ifdef RUBY_ASAN_ENABLED
6835+
rb_execution_context_t *ec = objspace->marking_machine_context_ec;
6836+
void *fake_frame_start;
6837+
void *fake_frame_end;
6838+
bool is_fake_frame = asan_get_fake_stack_extents(
6839+
ec->thread_ptr->asan_fake_stack_handle, obj,
6840+
ec->machine.stack_start, ec->machine.stack_end,
6841+
&fake_frame_start, &fake_frame_end
6842+
);
6843+
if (is_fake_frame) {
6844+
each_stack_location(objspace, ec, fake_frame_start, fake_frame_end, gc_mark_maybe);
6845+
}
6846+
#endif
6847+
}
6848+
68246849
#if defined(__wasm__)
68256850

68266851

@@ -6882,9 +6907,10 @@ mark_current_machine_context(rb_objspace_t *objspace, rb_execution_context_t *ec
68826907
SET_STACK_END;
68836908
GET_STACK_BOUNDS(stack_start, stack_end, 1);
68846909

6885-
each_location(objspace, save_regs_gc_mark.v, numberof(save_regs_gc_mark.v), gc_mark_maybe);
6886-
6887-
each_stack_location(objspace, ec, stack_start, stack_end, gc_mark_maybe);
6910+
objspace->marking_machine_context_ec = ec;
6911+
each_location(objspace, save_regs_gc_mark.v, numberof(save_regs_gc_mark.v), gc_mark_machine_stack_location_maybe);
6912+
each_stack_location(objspace, ec, stack_start, stack_end, gc_mark_machine_stack_location_maybe);
6913+
objspace->marking_machine_context_ec = NULL;
68886914
}
68896915
#endif
68906916

internal/sanitizers.h

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,4 +213,68 @@ asan_get_real_stack_addr(void* slot)
213213
return addr ? addr : slot;
214214
}
215215

216+
/*!
217+
* Gets the current thread's fake stack handle, which can be passed into get_fake_stack_extents
218+
*
219+
* \retval An opaque value which can be passed to asan_get_fake_stack_extents
220+
*/
221+
static inline void *
222+
asan_get_thread_fake_stack_handle(void)
223+
{
224+
return __asan_get_current_fake_stack();
225+
}
226+
227+
/*!
228+
* Checks if the given VALUE _actually_ represents a pointer to an ASAN fake stack.
229+
*
230+
* If the given slot _is_ actually a reference to an ASAN fake stack, and that fake stack
231+
* contains the real values for the passed-in range of machine stack addresses, returns true
232+
* and the range of the fake stack through the outparams.
233+
*
234+
* Otherwise, returns false, and sets the outparams to NULL.
235+
*
236+
* Note that this function expects "start" to be > "end" on downward-growing stack architectures;
237+
*
238+
* \param[in] thread_fake_stack_handle The asan fake stack reference for the thread we're scanning
239+
* \param[in] slot The value on the machine stack we want to inspect
240+
* \param[in] machine_stack_start The extents of the real machine stack on which slot lives
241+
* \param[in] machine_stack_end The extents of the real machine stack on which slot lives
242+
* \param[out] fake_stack_start_out The extents of the fake stack which contains real VALUEs
243+
* \param[out] fake_stack_end_out The extents of the fake stack which contains real VALUEs
244+
* \return Whether slot is a pointer to a fake stack for the given machine stack range
245+
*/
246+
247+
static inline bool
248+
asan_get_fake_stack_extents(void *thread_fake_stack_handle, VALUE slot,
249+
void *machine_stack_start, void *machine_stack_end,
250+
void **fake_stack_start_out, void **fake_stack_end_out)
251+
{
252+
/* the ifdef is needed here to suppress a warning about fake_frame_{start/end} being
253+
uninitialized if __asan_addr_is_in_fake_stack is an empty macro */
254+
#ifdef RUBY_ASAN_ENABLED
255+
void *fake_frame_start;
256+
void *fake_frame_end;
257+
void *real_stack_frame = __asan_addr_is_in_fake_stack(
258+
thread_fake_stack_handle, (void *)slot, &fake_frame_start, &fake_frame_end
259+
);
260+
if (real_stack_frame) {
261+
bool in_range;
262+
#if STACK_GROW_DIRECTION < 0
263+
in_range = machine_stack_start >= real_stack_frame && real_stack_frame >= machine_stack_end;
264+
#else
265+
in_range = machine_stack_start <= real_stack_frame && real_stack_frame <= machine_stack_end;
266+
#endif
267+
if (in_range) {
268+
*fake_stack_start_out = fake_frame_start;
269+
*fake_stack_end_out = fake_frame_end;
270+
return true;
271+
}
272+
}
273+
#endif
274+
*fake_stack_start_out = 0;
275+
*fake_stack_end_out = 0;
276+
return false;
277+
}
278+
279+
216280
#endif /* INTERNAL_SANITIZERS_H */

thread.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,9 @@ void
525525
ruby_thread_init_stack(rb_thread_t *th, void *local_in_parent_frame)
526526
{
527527
native_thread_init_stack(th, local_in_parent_frame);
528+
#ifdef RUBY_ASAN_ENABLED
529+
th->asan_fake_stack_handle = asan_get_thread_fake_stack_handle();
530+
#endif
528531
}
529532

530533
const VALUE *

vm_core.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ extern int ruby_assert_critical_section_entered;
9494
#include "internal.h"
9595
#include "internal/array.h"
9696
#include "internal/basic_operators.h"
97+
#include "internal/sanitizers.h"
9798
#include "internal/serial.h"
9899
#include "internal/vm.h"
99100
#include "method.h"
@@ -1155,6 +1156,11 @@ typedef struct rb_thread_struct {
11551156
void **specific_storage;
11561157

11571158
struct rb_ext_config ext_config;
1159+
1160+
#ifdef RUBY_ASAN_ENABLED
1161+
void *asan_fake_stack_handle;
1162+
#endif
1163+
11581164
} rb_thread_t;
11591165

11601166
static inline unsigned int

0 commit comments

Comments
 (0)