From c5c05460ac20abcbc0ed686eb4acf06da7a39a79 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Fri, 20 Sep 2019 19:06:22 -0700 Subject: Warn on access/modify of $SAFE, and remove effects of modifying $SAFE This removes the security features added by $SAFE = 1, and warns for access or modification of $SAFE from Ruby-level, as well as warning when calling all public C functions related to $SAFE. This modifies some internal functions that took a safe level argument to no longer take the argument. rb_require_safe now warns, rb_require_string has been added as a version that takes a VALUE and does not warn. One public C function that still takes a safe level argument and that this doesn't warn for is rb_eval_cmd. We may want to consider adding an alternative method that does not take a safe level argument, and warn for rb_eval_cmd. --- load.c | 68 +++++++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 38 insertions(+), 30 deletions(-) (limited to 'load.c') diff --git a/load.c b/load.c index b95d22848a..5b27fd1cc1 100644 --- a/load.c +++ b/load.c @@ -48,7 +48,6 @@ rb_construct_expanded_load_path(enum expand_type type, int *has_relative, int *h VALUE expanded_load_path = vm->expanded_load_path; VALUE ary; long i; - int level = rb_safe_level(); ary = rb_ary_tmp_new(RARRAY_LEN(load_path)); for (i = 0; i < RARRAY_LEN(load_path); ++i) { @@ -58,7 +57,7 @@ rb_construct_expanded_load_path(enum expand_type type, int *has_relative, int *h as_str = path = RARRAY_AREF(load_path, i); is_string = RB_TYPE_P(path, T_STRING) ? 1 : 0; non_cache = !is_string ? 1 : 0; - as_str = rb_get_path_check_to_string(path, level); + as_str = rb_get_path_check_to_string(path); as_cstr = RSTRING_PTR(as_str); if (!non_cache) { @@ -79,7 +78,7 @@ rb_construct_expanded_load_path(enum expand_type type, int *has_relative, int *h /* Freeze only string object. We expand other objects every time. */ if (is_string) rb_str_freeze(path); - as_str = rb_get_path_check_convert(path, as_str, level); + as_str = rb_get_path_check_convert(as_str); expanded_path = rb_check_realpath(Qnil, as_str); if (NIL_P(expanded_path)) expanded_path = as_str; rb_ary_push(ary, rb_fstring(expanded_path)); @@ -689,7 +688,7 @@ rb_f_load(int argc, VALUE *argv, VALUE _) rb_scan_args(argc, argv, "11", &fname, &wrap); - orig_fname = rb_get_path_check_to_string(fname, rb_safe_level()); + orig_fname = rb_get_path_check_to_string(fname); fname = rb_str_encode_ospath(orig_fname); RUBY_DTRACE_HOOK(LOAD_ENTRY, RSTRING_PTR(orig_fname)); @@ -809,7 +808,7 @@ load_unlock(const char *ftptr, int done) VALUE rb_f_require(VALUE obj, VALUE fname) { - return rb_require_safe(fname, rb_safe_level()); + return rb_require_string(fname); } /* @@ -828,13 +827,13 @@ rb_f_require_relative(VALUE obj, VALUE fname) rb_loaderror("cannot infer basepath"); } base = rb_file_dirname(base); - return rb_require_safe(rb_file_absolute_path(fname, base), rb_safe_level()); + return rb_require_string(rb_file_absolute_path(fname, base)); } typedef int (*feature_func)(const char *feature, const char *ext, int rb, int expanded, const char **fn); static int -search_required(VALUE fname, volatile VALUE *path, int safe_level, feature_func rb_feature_p) +search_required(VALUE fname, volatile VALUE *path, feature_func rb_feature_p) { VALUE tmp; char *ext, *ftptr; @@ -849,7 +848,7 @@ search_required(VALUE fname, volatile VALUE *path, int safe_level, feature_func if (loading) *path = rb_filesystem_str_new_cstr(loading); return 'r'; } - if ((tmp = rb_find_file_safe(fname, safe_level)) != 0) { + if ((tmp = rb_find_file(fname)) != 0) { ext = strrchr(ftptr = RSTRING_PTR(tmp), '.'); if (!rb_feature_p(ftptr, ext, TRUE, TRUE, &loading) || loading) *path = tmp; @@ -865,7 +864,7 @@ search_required(VALUE fname, volatile VALUE *path, int safe_level, feature_func tmp = rb_str_subseq(fname, 0, ext - RSTRING_PTR(fname)); #ifdef DLEXT2 OBJ_FREEZE(tmp); - if (rb_find_file_ext_safe(&tmp, loadable_ext + 1, safe_level)) { + if (rb_find_file_ext(&tmp, loadable_ext + 1)) { ext = strrchr(ftptr = RSTRING_PTR(tmp), '.'); if (!rb_feature_p(ftptr, ext, FALSE, TRUE, &loading) || loading) *path = tmp; @@ -874,7 +873,7 @@ search_required(VALUE fname, volatile VALUE *path, int safe_level, feature_func #else rb_str_cat2(tmp, DLEXT); OBJ_FREEZE(tmp); - if ((tmp = rb_find_file_safe(tmp, safe_level)) != 0) { + if ((tmp = rb_find_file(tmp)) != 0) { ext = strrchr(ftptr = RSTRING_PTR(tmp), '.'); if (!rb_feature_p(ftptr, ext, FALSE, TRUE, &loading) || loading) *path = tmp; @@ -887,7 +886,7 @@ search_required(VALUE fname, volatile VALUE *path, int safe_level, feature_func if (loading) *path = rb_filesystem_str_new_cstr(loading); return 's'; } - if ((tmp = rb_find_file_safe(fname, safe_level)) != 0) { + if ((tmp = rb_find_file(fname)) != 0) { ext = strrchr(ftptr = RSTRING_PTR(tmp), '.'); if (!rb_feature_p(ftptr, ext, FALSE, TRUE, &loading) || loading) *path = tmp; @@ -900,7 +899,7 @@ search_required(VALUE fname, volatile VALUE *path, int safe_level, feature_func return 'r'; } tmp = fname; - type = rb_find_file_ext_safe(&tmp, loadable_ext, safe_level); + type = rb_find_file_ext(&tmp, loadable_ext); switch (type) { case 0: if (ft) @@ -951,9 +950,9 @@ rb_resolve_feature_path(VALUE klass, VALUE fname) int found; VALUE sym; - fname = rb_get_path_check(fname, 0); + fname = rb_get_path(fname); path = rb_str_encode_ospath(fname); - found = search_required(path, &path, 0, no_feature_p); + found = search_required(path, &path, no_feature_p); switch (found) { case 'r': @@ -977,7 +976,7 @@ rb_resolve_feature_path(VALUE klass, VALUE fname) * >1: exception */ static int -require_internal(rb_execution_context_t *ec, VALUE fname, int safe, int exception) +require_internal(rb_execution_context_t *ec, VALUE fname, int exception) { volatile int result = -1; rb_thread_t *th = rb_ec_thread_ptr(ec); @@ -985,28 +984,22 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int safe, int exceptio volatile VALUE self = th->top_self; volatile VALUE errinfo = ec->errinfo; enum ruby_tag_type state; - struct { - int safe; - } volatile saved; char *volatile ftptr = 0; VALUE path; - fname = rb_get_path_check(fname, safe); + fname = rb_get_path(fname); path = rb_str_encode_ospath(fname); RUBY_DTRACE_HOOK(REQUIRE_ENTRY, RSTRING_PTR(fname)); EC_PUSH_TAG(ec); - saved.safe = rb_safe_level(); ec->errinfo = Qnil; /* ensure */ th->top_wrapper = 0; if ((state = EC_EXEC_TAG()) == TAG_NONE) { long handle; int found; - rb_set_safe_level_force(0); - RUBY_DTRACE_HOOK(FIND_REQUIRE_ENTRY, RSTRING_PTR(fname)); - found = search_required(path, &path, safe, rb_feature_p); + found = search_required(path, &path, rb_feature_p); RUBY_DTRACE_HOOK(FIND_REQUIRE_RETURN, RSTRING_PTR(fname)); if (found) { @@ -1040,7 +1033,6 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int safe, int exceptio th->top_wrapper = wrapper; if (ftptr) load_unlock(RSTRING_PTR(path), !state); - rb_set_safe_level_force(saved.safe); if (state) { if (exception) { /* usually state == TAG_RAISE only, except for @@ -1069,10 +1061,10 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int safe, int exceptio } int -rb_require_internal(VALUE fname, int safe) +rb_require_internal(VALUE fname) { rb_execution_context_t *ec = GET_EC(); - return require_internal(ec, fname, safe, 1); + return require_internal(ec, fname, 1); } int @@ -1081,16 +1073,33 @@ ruby_require_internal(const char *fname, unsigned int len) struct RString fake; VALUE str = rb_setup_fake_str(&fake, fname, len, 0); rb_execution_context_t *ec = GET_EC(); - int result = require_internal(ec, str, 0, 0); + int result = require_internal(ec, str, 0); rb_set_errinfo(Qnil); return result == TAG_RETURN ? 1 : result ? -1 : 0; } VALUE rb_require_safe(VALUE fname, int safe) +{ + rb_warn("rb_require_safe will be removed in Ruby 3.0"); + rb_execution_context_t *ec = GET_EC(); + int result = require_internal(ec, fname, 1); + + if (result > TAG_RETURN) { + EC_JUMP_TAG(ec, result); + } + if (result < 0) { + load_failed(fname); + } + + return result ? Qtrue : Qfalse; +} + +VALUE +rb_require_string(VALUE fname) { rb_execution_context_t *ec = GET_EC(); - int result = require_internal(ec, fname, safe, 1); + int result = require_internal(ec, fname, 1); if (result > TAG_RETURN) { EC_JUMP_TAG(ec, result); @@ -1105,8 +1114,7 @@ rb_require_safe(VALUE fname, int safe) VALUE rb_require(const char *fname) { - VALUE fn = rb_str_new_cstr(fname); - return rb_require_safe(fn, rb_safe_level()); + return rb_require_string(rb_str_new_cstr(fname)); } static int -- cgit v1.2.3