Skip to content

Commit 1be89ca

Browse files
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 ad7da36 commit 1be89ca

File tree

8 files changed

+157
-7
lines changed

8 files changed

+157
-7
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
@@ -592,6 +592,7 @@ ripper.o: $(top_srcdir)/internal/parse.h
592592
ripper.o: $(top_srcdir)/internal/rational.h
593593
ripper.o: $(top_srcdir)/internal/re.h
594594
ripper.o: $(top_srcdir)/internal/ruby_parser.h
595+
ripper.o: $(top_srcdir)/internal/sanitizers.h
595596
ripper.o: $(top_srcdir)/internal/serial.h
596597
ripper.o: $(top_srcdir)/internal/static_assert.h
597598
ripper.o: $(top_srcdir)/internal/string.h

ext/socket/depend

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ ancdata.o: $(top_srcdir)/internal/error.h
198198
ancdata.o: $(top_srcdir)/internal/gc.h
199199
ancdata.o: $(top_srcdir)/internal/imemo.h
200200
ancdata.o: $(top_srcdir)/internal/io.h
201+
ancdata.o: $(top_srcdir)/internal/sanitizers.h
201202
ancdata.o: $(top_srcdir)/internal/serial.h
202203
ancdata.o: $(top_srcdir)/internal/static_assert.h
203204
ancdata.o: $(top_srcdir)/internal/string.h
@@ -406,6 +407,7 @@ basicsocket.o: $(top_srcdir)/internal/error.h
406407
basicsocket.o: $(top_srcdir)/internal/gc.h
407408
basicsocket.o: $(top_srcdir)/internal/imemo.h
408409
basicsocket.o: $(top_srcdir)/internal/io.h
410+
basicsocket.o: $(top_srcdir)/internal/sanitizers.h
409411
basicsocket.o: $(top_srcdir)/internal/serial.h
410412
basicsocket.o: $(top_srcdir)/internal/static_assert.h
411413
basicsocket.o: $(top_srcdir)/internal/string.h
@@ -614,6 +616,7 @@ constants.o: $(top_srcdir)/internal/error.h
614616
constants.o: $(top_srcdir)/internal/gc.h
615617
constants.o: $(top_srcdir)/internal/imemo.h
616618
constants.o: $(top_srcdir)/internal/io.h
619+
constants.o: $(top_srcdir)/internal/sanitizers.h
617620
constants.o: $(top_srcdir)/internal/serial.h
618621
constants.o: $(top_srcdir)/internal/static_assert.h
619622
constants.o: $(top_srcdir)/internal/string.h
@@ -823,6 +826,7 @@ ifaddr.o: $(top_srcdir)/internal/error.h
823826
ifaddr.o: $(top_srcdir)/internal/gc.h
824827
ifaddr.o: $(top_srcdir)/internal/imemo.h
825828
ifaddr.o: $(top_srcdir)/internal/io.h
829+
ifaddr.o: $(top_srcdir)/internal/sanitizers.h
826830
ifaddr.o: $(top_srcdir)/internal/serial.h
827831
ifaddr.o: $(top_srcdir)/internal/static_assert.h
828832
ifaddr.o: $(top_srcdir)/internal/string.h
@@ -1031,6 +1035,7 @@ init.o: $(top_srcdir)/internal/error.h
10311035
init.o: $(top_srcdir)/internal/gc.h
10321036
init.o: $(top_srcdir)/internal/imemo.h
10331037
init.o: $(top_srcdir)/internal/io.h
1038+
init.o: $(top_srcdir)/internal/sanitizers.h
10341039
init.o: $(top_srcdir)/internal/serial.h
10351040
init.o: $(top_srcdir)/internal/static_assert.h
10361041
init.o: $(top_srcdir)/internal/string.h
@@ -1239,6 +1244,7 @@ ipsocket.o: $(top_srcdir)/internal/error.h
12391244
ipsocket.o: $(top_srcdir)/internal/gc.h
12401245
ipsocket.o: $(top_srcdir)/internal/imemo.h
12411246
ipsocket.o: $(top_srcdir)/internal/io.h
1247+
ipsocket.o: $(top_srcdir)/internal/sanitizers.h
12421248
ipsocket.o: $(top_srcdir)/internal/serial.h
12431249
ipsocket.o: $(top_srcdir)/internal/static_assert.h
12441250
ipsocket.o: $(top_srcdir)/internal/string.h
@@ -1447,6 +1453,7 @@ option.o: $(top_srcdir)/internal/error.h
14471453
option.o: $(top_srcdir)/internal/gc.h
14481454
option.o: $(top_srcdir)/internal/imemo.h
14491455
option.o: $(top_srcdir)/internal/io.h
1456+
option.o: $(top_srcdir)/internal/sanitizers.h
14501457
option.o: $(top_srcdir)/internal/serial.h
14511458
option.o: $(top_srcdir)/internal/static_assert.h
14521459
option.o: $(top_srcdir)/internal/string.h
@@ -1655,6 +1662,7 @@ raddrinfo.o: $(top_srcdir)/internal/error.h
16551662
raddrinfo.o: $(top_srcdir)/internal/gc.h
16561663
raddrinfo.o: $(top_srcdir)/internal/imemo.h
16571664
raddrinfo.o: $(top_srcdir)/internal/io.h
1665+
raddrinfo.o: $(top_srcdir)/internal/sanitizers.h
16581666
raddrinfo.o: $(top_srcdir)/internal/serial.h
16591667
raddrinfo.o: $(top_srcdir)/internal/static_assert.h
16601668
raddrinfo.o: $(top_srcdir)/internal/string.h
@@ -1863,6 +1871,7 @@ socket.o: $(top_srcdir)/internal/error.h
18631871
socket.o: $(top_srcdir)/internal/gc.h
18641872
socket.o: $(top_srcdir)/internal/imemo.h
18651873
socket.o: $(top_srcdir)/internal/io.h
1874+
socket.o: $(top_srcdir)/internal/sanitizers.h
18661875
socket.o: $(top_srcdir)/internal/serial.h
18671876
socket.o: $(top_srcdir)/internal/static_assert.h
18681877
socket.o: $(top_srcdir)/internal/string.h
@@ -2071,6 +2080,7 @@ sockssocket.o: $(top_srcdir)/internal/error.h
20712080
sockssocket.o: $(top_srcdir)/internal/gc.h
20722081
sockssocket.o: $(top_srcdir)/internal/imemo.h
20732082
sockssocket.o: $(top_srcdir)/internal/io.h
2083+
sockssocket.o: $(top_srcdir)/internal/sanitizers.h
20742084
sockssocket.o: $(top_srcdir)/internal/serial.h
20752085
sockssocket.o: $(top_srcdir)/internal/static_assert.h
20762086
sockssocket.o: $(top_srcdir)/internal/string.h
@@ -2279,6 +2289,7 @@ tcpserver.o: $(top_srcdir)/internal/error.h
22792289
tcpserver.o: $(top_srcdir)/internal/gc.h
22802290
tcpserver.o: $(top_srcdir)/internal/imemo.h
22812291
tcpserver.o: $(top_srcdir)/internal/io.h
2292+
tcpserver.o: $(top_srcdir)/internal/sanitizers.h
22822293
tcpserver.o: $(top_srcdir)/internal/serial.h
22832294
tcpserver.o: $(top_srcdir)/internal/static_assert.h
22842295
tcpserver.o: $(top_srcdir)/internal/string.h
@@ -2487,6 +2498,7 @@ tcpsocket.o: $(top_srcdir)/internal/error.h
24872498
tcpsocket.o: $(top_srcdir)/internal/gc.h
24882499
tcpsocket.o: $(top_srcdir)/internal/imemo.h
24892500
tcpsocket.o: $(top_srcdir)/internal/io.h
2501+
tcpsocket.o: $(top_srcdir)/internal/sanitizers.h
24902502
tcpsocket.o: $(top_srcdir)/internal/serial.h
24912503
tcpsocket.o: $(top_srcdir)/internal/static_assert.h
24922504
tcpsocket.o: $(top_srcdir)/internal/string.h
@@ -2695,6 +2707,7 @@ udpsocket.o: $(top_srcdir)/internal/error.h
26952707
udpsocket.o: $(top_srcdir)/internal/gc.h
26962708
udpsocket.o: $(top_srcdir)/internal/imemo.h
26972709
udpsocket.o: $(top_srcdir)/internal/io.h
2710+
udpsocket.o: $(top_srcdir)/internal/sanitizers.h
26982711
udpsocket.o: $(top_srcdir)/internal/serial.h
26992712
udpsocket.o: $(top_srcdir)/internal/static_assert.h
27002713
udpsocket.o: $(top_srcdir)/internal/string.h
@@ -2903,6 +2916,7 @@ unixserver.o: $(top_srcdir)/internal/error.h
29032916
unixserver.o: $(top_srcdir)/internal/gc.h
29042917
unixserver.o: $(top_srcdir)/internal/imemo.h
29052918
unixserver.o: $(top_srcdir)/internal/io.h
2919+
unixserver.o: $(top_srcdir)/internal/sanitizers.h
29062920
unixserver.o: $(top_srcdir)/internal/serial.h
29072921
unixserver.o: $(top_srcdir)/internal/static_assert.h
29082922
unixserver.o: $(top_srcdir)/internal/string.h
@@ -3111,6 +3125,7 @@ unixsocket.o: $(top_srcdir)/internal/error.h
31113125
unixsocket.o: $(top_srcdir)/internal/gc.h
31123126
unixsocket.o: $(top_srcdir)/internal/imemo.h
31133127
unixsocket.o: $(top_srcdir)/internal/io.h
3128+
unixsocket.o: $(top_srcdir)/internal/sanitizers.h
31143129
unixsocket.o: $(top_srcdir)/internal/serial.h
31153130
unixsocket.o: $(top_srcdir)/internal/static_assert.h
31163131
unixsocket.o: $(top_srcdir)/internal/string.h

gc.c

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6722,6 +6722,25 @@ static void each_stack_location(rb_objspace_t *objspace, const rb_execution_cont
67226722
const VALUE *stack_start, const VALUE *stack_end, void *ctx,
67236723
void (*cb)(rb_objspace_t *, void *, VALUE));
67246724

6725+
static void
6726+
gc_mark_machine_stack_location_maybe(rb_objspace_t *objspace, void *ctx, VALUE obj)
6727+
{
6728+
gc_mark_maybe(objspace, obj);
6729+
#ifdef RUBY_ASAN_ENABLED
6730+
rb_execution_context_t *ec = ctx;
6731+
void *fake_frame_start;
6732+
void *fake_frame_end;
6733+
bool is_fake_frame = asan_get_fake_stack_extents(
6734+
ec->thread_ptr->asan_fake_stack_handle, obj,
6735+
ec->machine.stack_start, ec->machine.stack_end,
6736+
&fake_frame_start, &fake_frame_end
6737+
);
6738+
if (is_fake_frame) {
6739+
each_stack_location(objspace, ec, fake_frame_start, fake_frame_end, NULL, gc_mark_maybe_cb);
6740+
}
6741+
#endif
6742+
}
6743+
67256744
#if defined(__wasm__)
67266745

67276746

@@ -6740,10 +6759,10 @@ static void
67406759
mark_current_machine_context(rb_objspace_t *objspace, rb_execution_context_t *ec)
67416760
{
67426761
emscripten_scan_stack(rb_mark_locations);
6743-
each_stack_location(objspace, ec, rb_stack_range_tmp[0], rb_stack_range_tmp[1], gc_mark_maybe_cb);
6762+
each_stack_location(objspace, ec, rb_stack_range_tmp[0], rb_stack_range_tmp[1], NULL, gc_mark_maybe_cb);
67446763

67456764
emscripten_scan_registers(rb_mark_locations);
6746-
each_stack_location(objspace, ec, rb_stack_range_tmp[0], rb_stack_range_tmp[1], gc_mark_maybe_cb);
6765+
each_stack_location(objspace, ec, rb_stack_range_tmp[0], rb_stack_range_tmp[1], NULL, gc_mark_maybe_cb);
67476766
}
67486767
# else // use Asyncify version
67496768

@@ -6753,10 +6772,10 @@ mark_current_machine_context(rb_objspace_t *objspace, rb_execution_context_t *ec
67536772
VALUE *stack_start, *stack_end;
67546773
SET_STACK_END;
67556774
GET_STACK_BOUNDS(stack_start, stack_end, 1);
6756-
each_stack_location(objspace, ec, stack_start, stack_end, gc_mark_maybe_cb);
6775+
each_stack_location(objspace, ec, stack_start, stack_end, NULL, gc_mark_maybe_cb);
67576776

67586777
rb_wasm_scan_locals(rb_mark_locations);
6759-
each_stack_location(objspace, ec, rb_stack_range_tmp[0], rb_stack_range_tmp[1], gc_mark_maybe_cb);
6778+
each_stack_location(objspace, ec, rb_stack_range_tmp[0], rb_stack_range_tmp[1], NULL, gc_mark_maybe_cb);
67606779
}
67616780

67626781
# endif
@@ -6783,9 +6802,9 @@ mark_current_machine_context(rb_objspace_t *objspace, rb_execution_context_t *ec
67836802
SET_STACK_END;
67846803
GET_STACK_BOUNDS(stack_start, stack_end, 1);
67856804

6786-
each_location(objspace, save_regs_gc_mark.v, numberof(save_regs_gc_mark.v), NULL, gc_mark_maybe_cb);
6805+
each_location(objspace, save_regs_gc_mark.v, numberof(save_regs_gc_mark.v), (void *)ec, gc_mark_machine_stack_location_maybe);
67876806

6788-
each_stack_location(objspace, ec, stack_start, stack_end, NULL, gc_mark_maybe_cb);
6807+
each_stack_location(objspace, ec, stack_start, stack_end, (void *)ec, gc_mark_machine_stack_location_maybe);
67896808
}
67906809
#endif
67916810

@@ -6803,7 +6822,7 @@ each_machine_stack_value(const rb_execution_context_t *ec, void *ctx, void (*cb)
68036822
void
68046823
rb_gc_mark_machine_stack(const rb_execution_context_t *ec)
68056824
{
6806-
each_machine_stack_value(ec, NULL, gc_mark_maybe_cb);
6825+
each_machine_stack_value(ec, (void *)ec, gc_mark_machine_stack_location_maybe);
68076826
}
68086827

68096828
static void

internal/sanitizers.h

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,4 +213,67 @@ 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+
216279
#endif /* INTERNAL_SANITIZERS_H */

thread.c

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

529532
const VALUE *

vm_core.h

Lines changed: 5 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"
@@ -1099,6 +1100,10 @@ typedef struct rb_thread_struct {
10991100
VALUE name;
11001101

11011102
struct rb_ext_config ext_config;
1103+
1104+
#ifdef RUBY_ASAN_ENABLED
1105+
void *asan_fake_stack_handle;
1106+
#endif
11021107
} rb_thread_t;
11031108

11041109
static inline unsigned int

0 commit comments

Comments
 (0)