Skip to content

Commit 913123c

Browse files
eregonfniephaus
authored andcommitted
[GR-66212] Use the wrapper at the right place where the Java->native call occurs
PullRequest: truffleruby/4570
2 parents c762d0f + 499d8f9 commit 913123c

File tree

7 files changed

+48
-50
lines changed

7 files changed

+48
-50
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ src/main/c/*/Makefile
4848
!src/main/c/truffleposix/Makefile
4949
!src/main/c/yarp/Makefile
5050
src/main/c/cext-trampoline/trampoline.c
51-
src/main/c/cext/wrappers.c
5251
src/main/c/etc/constdefs.h
5352
src/main/c/spawn-helper/spawn-helper
5453
src/main/c/debug

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Bug fixes:
99
* Fix `Time.new` with String argument and handle nanoseconds correctly (#3836, @andrykonchin).
1010
* Fix a possible case of infinite recursion when implementing `frozen?` in a native extension (@nirvdrum).
1111
* Fix parameters forwarding to a method call executed with `Kernel#eval` (@andrykonchin).
12+
* Fix segfaults in native extensions when the reference processing thread is interrupted and would `longjmp()` incorrectly (#3903, @eregon).
1213

1314
Compatibility:
1415

lib/cext/ABI_check.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1
1+
2

lib/truffle/truffle/cext.rb

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,16 @@ module Truffle::CExt
3838
end
3939

4040
SETUP_WRAPPERS = -> lib do
41-
# signature starts with an extra pointer which is for passing the native function to call
41+
# These wrappers must be used when calling native extension functions,
42+
# so that setjmp() is done before calling the native extension function,
43+
# and if the native extension function (potentially through other functions) calls a Ruby C API function
44+
# and if that throws a Java exception, then on return from that Ruby C API function a longjmp()
45+
# will be done to unwind the native stack.
46+
# It is essential that only native frames and no Java frames are unwinded with longjmp(),
47+
# which means these wrappers must be used exactly when calling into native functions, not before or after.
48+
# Sulong-executed C code counts as not-native in this context.
49+
50+
# The signature starts with an extra pointer which is for passing the native function to call
4251
POINTER_TO_POINTER_WRAPPERS = [nil] + (1..16).map do |n|
4352
sig = -"(pointer,#{(['pointer'] * n).join(',')}):pointer"
4453
Primitive.interop_eval_nfi(sig).bind(lib[:"rb_tr_setjmp_wrapper_pointer#{n}_to_pointer"])
@@ -1740,16 +1749,15 @@ def RTYPEDDATA(object)
17401749
if Truffle::Interop.null?(marker_function)
17411750
nil
17421751
else
1743-
-> { Primitive.interop_execute(POINTER_TO_VOID_WRAPPER, [marker_function, struct]) }
1752+
-> { Primitive.interop_execute(marker_function, [struct]) }
17441753
end
17451754
end
17461755

17471756
private def data_sizer(sizer_function, rtypeddata)
1748-
raise unless sizer_function.respond_to?(:call)
17491757
use_cext_lock = Primitive.use_cext_lock?
17501758
proc {
17511759
Primitive.call_with_cext_lock_and_frame(
1752-
POINTER_TO_SIZE_T_WRAPPER, [sizer_function, rtypeddata],
1760+
sizer_function, [rtypeddata],
17531761
Primitive.caller_special_variables_if_available, nil, use_cext_lock)
17541762
}
17551763
end
@@ -1792,7 +1800,7 @@ def rb_data_typed_object_wrap(ruby_class, data, data_type, mark, free, size)
17921800
end
17931801

17941802
def run_data_finalizer(function, data, use_cext_lock)
1795-
Primitive.call_with_cext_lock_and_frame POINTER_TO_VOID_WRAPPER, [function, data], nil, nil, use_cext_lock
1803+
Primitive.call_with_cext_lock_and_frame function, [data], nil, nil, use_cext_lock
17961804
end
17971805

17981806
def run_marker(obj)

src/main/c/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ all: $(TRAMPOLINE) $(LIBTRUFFLERUBY) openssl/openssl.$(DLEXT) zlib/zlib.$(DLEXT)
4343
clean: clean_cexts clean_trampoline
4444

4545
clean_trampoline:
46-
$(Q) rm -f $(TRAMPOLINE) cext-trampoline/trampoline.c cext/wrappers.c cext-trampoline/*.o
46+
$(Q) rm -f $(TRAMPOLINE) cext-trampoline/trampoline.c cext-trampoline/*.o
4747

4848
clean_cexts:
4949
$(Q) rm -f cext/Makefile cext/*.o $(LIBTRUFFLERUBY)

src/main/c/cext/data.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,30 @@ struct RTypedData* rb_tr_rtypeddata_create(const rb_data_type_t *data_type, void
2929
return result;
3030
}
3131

32+
void rb_tr_setjmp_wrapper_pointer1_to_void(void (*func)(VALUE arg), VALUE arg);
33+
size_t rb_tr_setjmp_wrapper_pointer1_to_size_t(size_t (*func)(const void *arg), const void *arg);
34+
3235
void rb_tr_rdata_run_marker(struct RData* rdata) {
3336
void* data = rdata->data;
3437
RUBY_DATA_FUNC dmark = rdata->dmark;
3538
if (data != NULL && dmark != NULL) {
36-
dmark(data);
39+
rb_tr_setjmp_wrapper_pointer1_to_void(dmark, data);
3740
}
3841
}
3942

4043
void rb_tr_rtypeddata_run_marker(struct RTypedData* rtypeddata) {
4144
void* data = rtypeddata->data;
4245
RUBY_DATA_FUNC dmark = rtypeddata->type->function.dmark;
4346
if (data != NULL && dmark != NULL) {
44-
dmark(data);
47+
rb_tr_setjmp_wrapper_pointer1_to_void(dmark, data);
4548
}
4649
}
4750

4851
size_t rb_tr_rtypeddata_run_memsizer(struct RTypedData* rtypeddata) {
4952
void* data = rtypeddata->data;
5053
size_t (*dsize)(const void *) = rtypeddata->type->function.dsize;
5154
if (data != NULL && dsize != NULL) {
52-
return dsize(data);
55+
return rb_tr_setjmp_wrapper_pointer1_to_size_t(dsize, data);
5356
} else {
5457
return 0;
5558
}
@@ -62,7 +65,7 @@ void rb_tr_rdata_run_finalizer(struct RData* rdata) {
6265
if (dfree == RUBY_DEFAULT_FREE) {
6366
ruby_xfree(data);
6467
} else if (dfree != NULL) {
65-
dfree(data);
68+
rb_tr_setjmp_wrapper_pointer1_to_void(dfree, data);
6669
}
6770
}
6871
// Also free the struct RData
@@ -76,7 +79,7 @@ void rb_tr_rtypeddata_run_finalizer(struct RTypedData* rtypeddata) {
7679
if (dfree == RUBY_DEFAULT_FREE) {
7780
ruby_xfree(data);
7881
} else if (dfree != NULL) {
79-
dfree(data);
82+
rb_tr_setjmp_wrapper_pointer1_to_void(dfree, data);
8083
}
8184
} // Also free the struct RTypedData
8285
free(rtypeddata);

tool/generate-cext-trampoline.rb

Lines changed: 24 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
functions = []
4949

5050
Dir["src/main/c/cext/*.c"].sort.each do |file|
51-
next if %w[cext_constants.c wrappers.c ruby.c st.c strlcpy.c].include?(File.basename(file))
51+
next if %w[cext_constants.c ruby.c st.c strlcpy.c].include?(File.basename(file))
5252

5353
contents = File.read(file)
5454
found_functions = contents.scan(function_regexp)
@@ -128,37 +128,31 @@ def struct_by_value?(type)
128128
129129
C
130130

131-
File.open("src/main/c/cext/wrappers.c", "w") do |sulong|
132-
sulong.puts <<C
133-
#include <ruby.h>
134-
135-
C
136-
137-
signatures = [
138-
['rb_tr_setjmp_wrapper_void_to_void', '(void):void'],
139-
['rb_tr_setjmp_wrapper_pointer1_to_void', '(VALUE arg):void'],
140-
['rb_tr_setjmp_wrapper_pointer2_to_void', '(VALUE tracepoint, void *data):void'],
141-
['rb_tr_setjmp_wrapper_pointer3_to_void', '(VALUE val, ID id, VALUE *data):void'],
142-
['rb_tr_setjmp_wrapper_pointer3_to_int', '(VALUE key, VALUE val, VALUE arg):int'],
143-
['rb_tr_setjmp_wrapper_pointer1_to_size_t', '(const void *arg):size_t'],
144-
['rb_tr_setjmp_wrapper_int_pointer2_to_pointer', '(int argc, VALUE *argv, VALUE obj):VALUE'],
145-
['rb_tr_setjmp_wrapper_pointer2_int_to_pointer', '(VALUE g, VALUE h, int r):VALUE'],
146-
['rb_tr_setjmp_wrapper_pointer2_int_pointer2_to_pointer', '(VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE *argv, VALUE blockarg):VALUE'],
131+
signatures = [
132+
['rb_tr_setjmp_wrapper_void_to_void', '(void):void'],
133+
['rb_tr_setjmp_wrapper_pointer1_to_void', '(VALUE arg):void'],
134+
['rb_tr_setjmp_wrapper_pointer2_to_void', '(VALUE tracepoint, void *data):void'],
135+
['rb_tr_setjmp_wrapper_pointer3_to_void', '(VALUE val, ID id, VALUE *data):void'],
136+
['rb_tr_setjmp_wrapper_pointer3_to_int', '(VALUE key, VALUE val, VALUE arg):int'],
137+
['rb_tr_setjmp_wrapper_pointer1_to_size_t', '(const void *arg):size_t'],
138+
['rb_tr_setjmp_wrapper_int_pointer2_to_pointer', '(int argc, VALUE *argv, VALUE obj):VALUE'],
139+
['rb_tr_setjmp_wrapper_pointer2_int_to_pointer', '(VALUE g, VALUE h, int r):VALUE'],
140+
['rb_tr_setjmp_wrapper_pointer2_int_pointer2_to_pointer', '(VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE *argv, VALUE blockarg):VALUE'],
141+
]
142+
(1..16).each do |arity|
143+
signatures << [
144+
"rb_tr_setjmp_wrapper_pointer#{arity}_to_pointer",
145+
"(#{(1..arity).map { |i| "VALUE arg#{i}" }.join(', ')}):VALUE"
147146
]
148-
(1..16).each do |arity|
149-
signatures << [
150-
"rb_tr_setjmp_wrapper_pointer#{arity}_to_pointer",
151-
"(#{(1..arity).map { |i| "VALUE arg#{i}" }.join(', ')}):VALUE"
152-
]
153-
end
147+
end
154148

155-
signatures.each do |function_name, signature|
156-
argument_types, return_type = signature.split(':')
157-
argument_types = argument_types.delete_prefix('(').delete_suffix(')')
158-
original_argument_types = argument_types
159-
argument_types = '' if argument_types == 'void'
160-
void = return_type == 'void'
161-
f.puts <<C
149+
signatures.each do |function_name, signature|
150+
argument_types, return_type = signature.split(':')
151+
argument_types = argument_types.delete_prefix('(').delete_suffix(')')
152+
original_argument_types = argument_types
153+
argument_types = '' if argument_types == 'void'
154+
void = return_type == 'void'
155+
f.puts <<C
162156
#{return_type} #{function_name}(#{return_type} (*func)(#{original_argument_types})#{', ' unless argument_types.empty?}#{argument_types}) {
163157
#{"#{return_type} result;" unless void}
164158
@@ -179,13 +173,6 @@ def struct_by_value?(type)
179173
}
180174
181175
C
182-
sulong.puts <<C
183-
#{return_type} #{function_name}(#{return_type} (*func)(#{original_argument_types})#{', ' unless argument_types.empty?}#{argument_types}) {
184-
#{'return ' unless void}func(#{argument_types.split(', ').map { |param| param[/\w+$/] }.join(', ')});
185-
}
186-
187-
C
188-
end
189176
end
190177

191178
f.puts "\n// Functions\n\n"

0 commit comments

Comments
 (0)