Load external GC using command line argument

This commit changes the external GC to be loaded with the `--gc-library`
command line argument instead of the RUBY_GC_LIBRARY_PATH environment
variable because @nobu pointed out that loading binaries using environment
variables can pose a security risk.
This commit is contained in:
Peter Zhu 2024-06-20 13:25:30 -04:00
parent bd583ca645
commit 90763e04ba
4 changed files with 38 additions and 30 deletions

42
gc.c
View File

@ -1862,31 +1862,44 @@ static void *rb_gc_impl_objspace_alloc(void);
#if USE_SHARED_GC #if USE_SHARED_GC
# include "dln.h" # include "dln.h"
# define RUBY_GC_LIBRARY_PATH "RUBY_GC_LIBRARY_PATH" typedef struct gc_function_map {
void *(*objspace_alloc)(void);
} rb_gc_function_map_t;
static void static rb_gc_function_map_t rb_gc_functions;
ruby_external_gc_init(void)
# define RUBY_GC_LIBRARY_ARG "--gc-library="
void
ruby_load_external_gc_from_argv(int argc, char **argv)
{ {
char *gc_so_path = getenv(RUBY_GC_LIBRARY_PATH); char *gc_so_path = NULL;
for (int i = 0; i < argc; i++) {
if (strncmp(argv[i], RUBY_GC_LIBRARY_ARG, sizeof(RUBY_GC_LIBRARY_ARG) - 1) == 0) {
gc_so_path = argv[i] + sizeof(RUBY_GC_LIBRARY_ARG) - 1;
}
}
void *handle = NULL; void *handle = NULL;
if (gc_so_path && dln_supported_p()) { if (gc_so_path && dln_supported_p()) {
char error[1024]; char error[1024];
handle = dln_open(gc_so_path, error, sizeof(error)); handle = dln_open(gc_so_path, error, sizeof(error));
if (!handle) { if (!handle) {
fprintf(stderr, "%s", error); fprintf(stderr, "%s", error);
rb_bug("ruby_external_gc_init: Shared library %s cannot be opened", gc_so_path); rb_bug("ruby_load_external_gc_from_argv: Shared library %s cannot be opened", gc_so_path);
} }
} }
# define load_external_gc_func(name) do { \ # define load_external_gc_func(name) do { \
if (handle) { \ if (handle) { \
rb_gc_functions->name = dln_symbol(handle, "rb_gc_impl_" #name); \ rb_gc_functions.name = dln_symbol(handle, "rb_gc_impl_" #name); \
if (!rb_gc_functions->name) { \ if (!rb_gc_functions.name) { \
rb_bug("ruby_external_gc_init: " #name " func not exported by library %s", gc_so_path); \ rb_bug("ruby_load_external_gc_from_argv: " #name " func not exported by library %s", gc_so_path); \
} \ } \
} \ } \
else { \ else { \
rb_gc_functions->name = rb_gc_impl_##name; \ rb_gc_functions.name = rb_gc_impl_##name; \
} \ } \
} while (0) } while (0)
@ -1895,15 +1908,12 @@ ruby_external_gc_init(void)
# undef load_external_gc_func # undef load_external_gc_func
} }
# define rb_gc_impl_objspace_alloc rb_gc_functions->objspace_alloc # define rb_gc_impl_objspace_alloc rb_gc_functions.objspace_alloc
#endif #endif
rb_objspace_t * rb_objspace_t *
rb_objspace_alloc(void) rb_objspace_alloc(void)
{ {
#if USE_SHARED_GC
ruby_external_gc_init();
#endif
return (rb_objspace_t *)rb_gc_impl_objspace_alloc(); return (rb_objspace_t *)rb_gc_impl_objspace_alloc();
} }
@ -13638,12 +13648,6 @@ rb_gcdebug_remove_stress_to_class(int argc, VALUE *argv, VALUE self)
void void
Init_GC(void) Init_GC(void)
{ {
#if USE_SHARED_GC
if (getenv(RUBY_GC_LIBRARY_PATH) != NULL && !dln_supported_p()) {
rb_warn(RUBY_GC_LIBRARY_PATH " is ignored because this executable file can't load extension libraries");
}
#endif
#undef rb_intern #undef rb_intern
malloc_offset = gc_compute_malloc_offset(); malloc_offset = gc_compute_malloc_offset();

5
main.c
View File

@ -32,10 +32,15 @@
# undef RUBY_DEBUG_ENV # undef RUBY_DEBUG_ENV
#endif #endif
void ruby_load_external_gc_from_argv(int argc, char **argv);
static int static int
rb_main(int argc, char **argv) rb_main(int argc, char **argv)
{ {
RUBY_INIT_STACK; RUBY_INIT_STACK;
#if USE_SHARED_GC
ruby_load_external_gc_from_argv(argc, argv);
#endif
ruby_init(); ruby_init();
return ruby_run_node(ruby_options(argc, argv)); return ruby_run_node(ruby_options(argc, argv));
} }

10
ruby.c
View File

@ -1435,6 +1435,16 @@ proc_long_options(ruby_cmdline_options_t *opt, const char *s, long argc, char **
else if (is_option_with_arg("source-encoding", Qfalse, Qtrue)) { else if (is_option_with_arg("source-encoding", Qfalse, Qtrue)) {
set_source_encoding_once(opt, s, 0); set_source_encoding_once(opt, s, 0);
} }
#endif
#if defined(USE_SHARED_GC) && USE_SHARED_GC
else if (is_option_with_arg("gc-library", Qfalse, Qfalse)) {
// no-op
// Handled by ruby_load_external_gc_from_argv
if (!dln_supported_p()) {
rb_warn("--gc-library is ignored because this executable file can't load extension libraries");
}
}
#endif #endif
else if (strcmp("version", s) == 0) { else if (strcmp("version", s) == 0) {
if (envopt) goto noenvopt_long; if (envopt) goto noenvopt_long;

View File

@ -106,14 +106,6 @@ extern int ruby_assert_critical_section_entered;
#include "ruby/thread_native.h" #include "ruby/thread_native.h"
#if USE_SHARED_GC
typedef struct gc_function_map {
void *(*objspace_alloc)(void);
} rb_gc_function_map_t;
#define rb_gc_functions (&GET_VM()->gc_functions_map)
#endif
/* /*
* implementation selector of get_insn_info algorithm * implementation selector of get_insn_info algorithm
* 0: linear search * 0: linear search
@ -761,9 +753,6 @@ typedef struct rb_vm_struct {
int coverage_mode; int coverage_mode;
struct rb_objspace *objspace; struct rb_objspace *objspace;
#if USE_SHARED_GC
rb_gc_function_map_t gc_functions_map;
#endif
rb_at_exit_list *at_exit; rb_at_exit_list *at_exit;