Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First base and research for ruby loader thread safety. #23

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 108 additions & 9 deletions source/loaders/rb_loader/source/rb_loader_impl.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,51 @@

#include <ruby.h>

/* TODO: Make mprotect cross-platform */
/* https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualprotect */
/* http://pubs.opengroup.org/onlinepubs/009695399/functions/mprotect.html */

/* TODO: Secure stack */
/* https://github.com/whitequark/coldruby/blob/master/libcoldruby/MRIRubyCompiler.cpp */

/* TODO: Bind stack */
/* https://github.com/sunaku/ruby-coroutine-example/blob/master/main.c */

/* TODO: Protect against memory access from other threads */
/* https://www.ruby-forum.com/t/ruby-embed-called-from-a-pthread/208641/6 */
/* https://redmine.ruby-lang.org/issues/2294#note-18 */
/* https://redmine.ruby-lang.org/issues/2294 */

#ifdef STACK_END_ADDRESS
#include <sys/mman.h> /* TODO: Make this cross-platform */
#endif

#ifdef STACK_END_ADDRESS
extern void *STACK_END_ADDRESS;

#define RUBY_PROLOGUE \
do { \
char stack_dummy;\
do {\
void *stack_backup = STACK_END_ADDRESS; \
STACK_END_ADDRESS = &stack_dummy;

#define RUBY_EPILOGUE \
STACK_END_ADDRESS = stack_backup; \
} while(0); \
} while(0);
#else
#define RUBY_PROLOGUE
#define RUBY_EPILOGUE
#endif

#define LOADER_IMPL_RB_FUNCTION_ARGS_SIZE 0x10
#define LOADER_IMPL_RB_PROTECT_ARGS_SIZE 0x10

#ifdef RUBY_GLOBAL_SETUP
RUBY_GLOBAL_SETUP
#endif

typedef struct loader_impl_rb_module_type
{
ID id;
Expand Down Expand Up @@ -179,11 +221,15 @@ function_return function_rb_interface_invoke(function func, function_impl impl,

VALUE result_value = Qnil;

value v = NULL;

if (args_size > LOADER_IMPL_RB_FUNCTION_ARGS_SIZE)
{
return NULL;
}

RUBY_PROLOGUE

if (args_size > 0)
{
enum function_rb_interface_invoke_id
Expand Down Expand Up @@ -317,16 +363,14 @@ function_return function_rb_interface_invoke(function func, function_impl impl,

if (result_value != Qnil)
{
value v = NULL;

const char * v_type_name = rb_type_deserialize(result_value, &v);

signature_set_return(s, loader_impl_type(rb_function->impl, v_type_name));

return v;
}

return NULL;
RUBY_EPILOGUE

return v;
}

void function_rb_interface_destroy(function func, function_impl impl)
Expand Down Expand Up @@ -404,11 +448,28 @@ loader_impl_data rb_loader_impl_initialize(loader_impl impl, configuration confi
NULL
};

struct rb_loader_impl_type * rb_impl = &rb_loader_impl_unused;

#ifdef STACK_END_ADDRESS
int page = sysconf(_SC_PAGE_SIZE);
mprotect((void *)((unsigned long)&STACK_END_ADDRESS & ~(page - 1)), page, PROT_READ | PROT_WRITE | PROT_EXEC);
#endif

(void)impl;
(void)config;

log_copy(host->log);

RUBY_PROLOGUE

#ifdef HAVE_RUBY_SYSINIT
int argc = 0;
char * argv[] = { "" };
ruby_sysinit(&argc, &argv);
#endif

RUBY_INIT_STACK;

ruby_init();

ruby_init_loadpath();
Expand All @@ -417,23 +478,27 @@ loader_impl_data rb_loader_impl_initialize(loader_impl impl, configuration confi
{
ruby_cleanup(0);

return NULL;
rb_impl = NULL;
}

if (rb_gv_set("$VERBOSE", Qtrue) != Qtrue)
{
ruby_cleanup(0);

return NULL;
rb_impl = NULL;
}

RUBY_EPILOGUE

log_write("metacall", LOG_LEVEL_DEBUG, "Ruby loader initialized correctly");

return (loader_impl_data)&rb_loader_impl_unused;
return (loader_impl_data)rb_impl;
}

int rb_loader_impl_execution_path(loader_impl impl, const loader_naming_path path)
{
RUBY_PROLOGUE

VALUE load_path_array = rb_gv_get("$:");

VALUE path_value = rb_str_new_cstr(path);
Expand All @@ -442,6 +507,8 @@ int rb_loader_impl_execution_path(loader_impl impl, const loader_naming_path pat

rb_ary_push(load_path_array, path_value);

RUBY_EPILOGUE

return 0;
}

Expand Down Expand Up @@ -643,6 +710,8 @@ loader_handle rb_loader_impl_load_from_file(loader_impl impl, const loader_namin
return NULL;
}

RUBY_PROLOGUE

for (iterator = 0; iterator < size; ++iterator)
{
loader_impl_rb_module rb_module;
Expand All @@ -663,6 +732,8 @@ loader_handle rb_loader_impl_load_from_file(loader_impl impl, const loader_namin
vector_push_back(handle->modules, &rb_module);
}

RUBY_EPILOGUE

return (loader_handle)handle;
}

Expand Down Expand Up @@ -755,8 +826,12 @@ loader_handle rb_loader_impl_load_from_memory(loader_impl impl, const loader_nam
return NULL;
}

RUBY_PROLOGUE

rb_module = rb_loader_impl_load_from_memory_module(impl, name, buffer, size);

RUBY_EPILOGUE

if (rb_module == NULL)
{
log_write("metacall", LOG_LEVEL_ERROR, "Invalid ruby module loading from memory");
Expand All @@ -777,9 +852,13 @@ loader_handle rb_loader_impl_load_from_package(loader_impl impl, const loader_na
{
/* TODO */

RUBY_PROLOGUE

(void)impl;
(void)path;

RUBY_EPILOGUE

return NULL;
}

Expand Down Expand Up @@ -814,6 +893,8 @@ int rb_loader_impl_clear(loader_impl impl, loader_handle handle)

size = vector_size(rb_handle->modules);

RUBY_PROLOGUE

for (iterator = 0; iterator < size; ++iterator)
{
loader_impl_rb_module * rb_module = vector_at(rb_handle->modules, iterator);
Expand All @@ -833,6 +914,8 @@ int rb_loader_impl_clear(loader_impl impl, loader_handle handle)
set_destroy((*rb_module)->function_map);
}

RUBY_EPILOGUE

vector_destroy(rb_handle->modules);

free(rb_handle);
Expand Down Expand Up @@ -867,10 +950,14 @@ loader_impl_rb_function rb_function_create(loader_impl_rb_module rb_module, ID i

if (rb_function != NULL)
{
RUBY_PROLOGUE

rb_function->rb_module = rb_module;
rb_function->method_id = id;
rb_function->args_hash = rb_hash_new();

RUBY_EPILOGUE

return rb_function;
}

Expand Down Expand Up @@ -945,6 +1032,8 @@ int rb_loader_impl_discover(loader_impl impl, loader_handle handle, context ctx)

int result = 0;

RUBY_PROLOGUE

for (iterator = 0; iterator < size; ++iterator)
{
loader_impl_rb_module * rb_module = vector_at(rb_handle->modules, iterator);
Expand All @@ -955,12 +1044,22 @@ int rb_loader_impl_discover(loader_impl impl, loader_handle handle, context ctx)
}
}

RUBY_EPILOGUE

return result;
}

int rb_loader_impl_destroy(loader_impl impl)
{
int result;

(void)impl;

return ruby_cleanup(0);
RUBY_PROLOGUE

result = ruby_cleanup(0);

RUBY_EPILOGUE

return result;
}
16 changes: 10 additions & 6 deletions source/ports/node_port/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,16 @@ describe('metacall', () => {
/* TODO: This creates a segmentation fault, it seems due to lack of thread-safety */
/*
54: -- C level backtrace information -------------------------------------------
54: /usr/lib/x86_64-linux-gnu/libruby-2.3.so.2.3 [0x7fcfd8d27025]
54: /usr/lib/x86_64-linux-gnu/libruby-2.3.so.2.3 [0x7fcfd8d2725c]
54: /usr/lib/x86_64-linux-gnu/libruby-2.3.so.2.3 [0x7fcfd8c00904]
54: /usr/lib/x86_64-linux-gnu/libruby-2.3.so.2.3 [0x7fcfd8cb281e]
54: /lib/x86_64-linux-gnu/libpthread.so.0 [0x7fcfdd60a0e0]
54: [0x3089380fa9ff]
54: /usr/lib/x86_64-linux-gnu/libruby-2.3.so.2.3 [0x7f1c3b04a025]
54: /usr/lib/x86_64-linux-gnu/libruby-2.3.so.2.3 [0x7f1c3b04a25c]
54: /usr/lib/x86_64-linux-gnu/libruby-2.3.so.2.3 [0x7f1c3af23904]
54: /usr/lib/x86_64-linux-gnu/libruby-2.3.so.2.3 [0x7f1c3afd581e]
54: /lib/x86_64-linux-gnu/libpthread.so.0 [0x7f1c4b92e0e0]
54: /usr/bin/node [0x8d5ea0]
54: /usr/bin/node [0x8d660c]
54: /usr/bin/node(_ZN2v88internal25FunctionCallbackArguments4CallEPFvRKNS_20FunctionCallbackInfoINS_5ValueEEEE+0x193) [0xa94a43]
54: /usr/bin/node [0xb0bbec]
54: /usr/bin/node(_ZN2v88internal21Builtin_HandleApiCallEiPPNS0_6ObjectEPNS0_7IsolateE+0xaf) [0xb0c83f]
*/
/* assert.strictEqual(metacall('get_second', 5, 12), 12); */
});
Expand Down