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. --- dir.c | 1 - encoding.c | 2 +- error.c | 6 - eval.c | 1 - ext/etc/etc.c | 2 - ext/io/console/console.c | 1 - ext/openssl/ossl_rand.c | 8 -- ext/openssl/ossl_x509store.c | 2 - ext/readline/readline.c | 1 - ext/socket/constants.c | 1 - ext/socket/raddrinfo.c | 8 +- ext/socket/unixsocket.c | 1 - ext/syslog/syslog.c | 1 - ext/win32ole/win32ole.c | 12 -- ext/win32ole/win32ole_event.c | 4 - file.c | 64 +++------ gc.c | 10 +- hash.c | 2 - include/ruby/intern.h | 7 +- include/ruby/ruby.h | 16 +-- internal.h | 9 +- io.c | 3 - iseq.c | 10 -- lib/debug.rb | 5 - lib/drb/drb.rb | 62 ++------- lib/erb.rb | 20 +-- lib/net/smtp.rb | 6 - lib/tempfile.rb | 4 - lib/tmpdir.rb | 34 ++--- load.c | 68 +++++----- parse.y | 2 +- proc.c | 29 ----- process.c | 12 -- ruby.c | 56 +++----- safe.c | 36 +++++- signal.c | 10 +- spec/ruby/language/predefined_spec.rb | 2 - spec/ruby/language/safe_spec.rb | 201 +++++++++++++++-------------- spec/ruby/optional/capi/string_spec.rb | 26 ++-- test/net/imap/test_imap_response_parser.rb | 13 -- test/pathname/test_pathname.rb | 10 -- test/readline/test_readline.rb | 15 --- test/ruby/test_alias.rb | 6 - test/ruby/test_file.rb | 12 -- test/ruby/test_optimization.rb | 11 -- test/ruby/test_proc.rb | 39 ------ test/ruby/test_require.rb | 7 - test/ruby/test_rubyoptions.rb | 14 -- test/ruby/test_thread.rb | 17 --- test/test_tempfile.rb | 11 -- test/test_tmpdir.rb | 13 -- test/win32ole/test_win32ole.rb | 49 ------- test/win32ole/test_win32ole_event.rb | 15 --- thread.c | 3 +- tool/lib/leakchecker.rb | 5 - transcode.c | 2 +- variable.c | 25 +--- vm_core.h | 1 - vm_eval.c | 9 +- 59 files changed, 277 insertions(+), 745 deletions(-) diff --git a/dir.c b/dir.c index 5221490b44..cefb5e7475 100644 --- a/dir.c +++ b/dir.c @@ -2719,7 +2719,6 @@ rb_push_glob(VALUE str, VALUE base, int flags) /* '\0' is delimiter */ rb_raise(rb_eArgError, "nul-separated glob pattern is deprecated"); } else { - rb_check_safe_obj(str); rb_enc_check(str, rb_enc_from_encoding(rb_usascii_encoding())); } ary = rb_ary_new(); diff --git a/encoding.c b/encoding.c index c1369080a5..b000e0f4a9 100644 --- a/encoding.c +++ b/encoding.c @@ -654,7 +654,7 @@ load_encoding(const char *name) ruby_verbose = Qfalse; ruby_debug = Qfalse; errinfo = rb_errinfo(); - loaded = rb_require_internal(enclib, rb_safe_level()); + loaded = rb_require_internal(enclib); ruby_verbose = verbose; ruby_debug = debug; rb_set_errinfo(errinfo); diff --git a/error.c b/error.c index 7a88ccbfd1..6ce49e0aa2 100644 --- a/error.c +++ b/error.c @@ -2985,12 +2985,6 @@ rb_check_copyable(VALUE obj, VALUE orig) if (!FL_ABLE(obj)) return; rb_check_frozen_internal(obj); if (!FL_ABLE(orig)) return; - if ((~RBASIC(obj)->flags & RBASIC(orig)->flags) & FL_TAINT) { - if (rb_safe_level() > 0) { - rb_raise(rb_eSecurityError, "Insecure: can't modify %"PRIsVALUE, - RBASIC(obj)->klass); - } - } } void diff --git a/eval.c b/eval.c index 64149d2c18..77b0efa0fc 100644 --- a/eval.c +++ b/eval.c @@ -204,7 +204,6 @@ rb_ec_cleanup(rb_execution_context_t *ec, volatile int ex) th = th0; errs[1] = ec->errinfo; if (THROW_DATA_P(ec->errinfo)) ec->errinfo = Qnil; - rb_set_safe_level_force(0); ruby_init_stack(&errs[STACK_UPPER(errs, 0, 1)]); SAVE_ROOT_JMPBUF(th, rb_ec_teardown(ec)); diff --git a/ext/etc/etc.c b/ext/etc/etc.c index 28761df8c1..1bb10e0b38 100644 --- a/ext/etc/etc.c +++ b/ext/etc/etc.c @@ -219,7 +219,6 @@ etc_getpwnam(VALUE obj, VALUE nam) struct passwd *pwd; const char *p = StringValueCStr(nam); - rb_check_safe_obj(nam); pwd = getpwnam(p); if (pwd == 0) rb_raise(rb_eArgError, "can't find user for %"PRIsVALUE, nam); return setup_passwd(pwd); @@ -463,7 +462,6 @@ etc_getgrnam(VALUE obj, VALUE nam) struct group *grp; const char *p = StringValueCStr(nam); - rb_check_safe_obj(nam); grp = getgrnam(p); if (grp == 0) rb_raise(rb_eArgError, "can't find group for %"PRIsVALUE, nam); return setup_group(grp); diff --git a/ext/io/console/console.c b/ext/io/console/console.c index 4f0470940a..42b000fc30 100644 --- a/ext/io/console/console.c +++ b/ext/io/console/console.c @@ -1483,7 +1483,6 @@ prompt(int argc, VALUE *argv, VALUE io) if (argc > 0 && !NIL_P(argv[0])) { VALUE str = argv[0]; StringValueCStr(str); - rb_check_safe_obj(str); rb_io_write(io, str); } } diff --git a/ext/openssl/ossl_rand.c b/ext/openssl/ossl_rand.c index c95857060a..4a4f9dd5bf 100644 --- a/ext/openssl/ossl_rand.c +++ b/ext/openssl/ossl_rand.c @@ -67,8 +67,6 @@ ossl_rand_add(VALUE self, VALUE str, VALUE entropy) static VALUE ossl_rand_load_file(VALUE self, VALUE filename) { - rb_check_safe_obj(filename); - if(!RAND_load_file(StringValueCStr(filename), -1)) { ossl_raise(eRandomError, NULL); } @@ -86,8 +84,6 @@ ossl_rand_load_file(VALUE self, VALUE filename) static VALUE ossl_rand_write_file(VALUE self, VALUE filename) { - rb_check_safe_obj(filename); - if (RAND_write_file(StringValueCStr(filename)) == -1) { ossl_raise(eRandomError, NULL); } @@ -164,8 +160,6 @@ ossl_rand_pseudo_bytes(VALUE self, VALUE len) static VALUE ossl_rand_egd(VALUE self, VALUE filename) { - rb_check_safe_obj(filename); - if (RAND_egd(StringValueCStr(filename)) == -1) { ossl_raise(eRandomError, NULL); } @@ -186,8 +180,6 @@ ossl_rand_egd_bytes(VALUE self, VALUE filename, VALUE len) { int n = NUM2INT(len); - rb_check_safe_obj(filename); - if (RAND_egd_bytes(StringValueCStr(filename), n) == -1) { ossl_raise(eRandomError, NULL); } diff --git a/ext/openssl/ossl_x509store.c b/ext/openssl/ossl_x509store.c index 2909eeda17..61543d44f6 100644 --- a/ext/openssl/ossl_x509store.c +++ b/ext/openssl/ossl_x509store.c @@ -304,7 +304,6 @@ ossl_x509store_add_file(VALUE self, VALUE file) char *path = NULL; if(file != Qnil){ - rb_check_safe_obj(file); path = StringValueCStr(file); } GetX509Store(self, store); @@ -340,7 +339,6 @@ ossl_x509store_add_path(VALUE self, VALUE dir) char *path = NULL; if(dir != Qnil){ - rb_check_safe_obj(dir); path = StringValueCStr(dir); } GetX509Store(self, store); diff --git a/ext/readline/readline.c b/ext/readline/readline.c index b686f99645..646be2b194 100644 --- a/ext/readline/readline.c +++ b/ext/readline/readline.c @@ -95,7 +95,6 @@ static char **readline_attempted_completion_function(const char *text, #define OutputStringValue(str) do {\ StringValueCStr(str);\ - rb_check_safe_obj(str);\ (str) = rb_str_conv_enc((str), rb_enc_get(str), rb_locale_encoding());\ } while (0)\ diff --git a/ext/socket/constants.c b/ext/socket/constants.c index 6fc862777e..1bbb53b173 100644 --- a/ext/socket/constants.c +++ b/ext/socket/constants.c @@ -28,7 +28,6 @@ constant_arg(VALUE arg, int (*str_to_int)(const char*, long, int*), const char * else if (!NIL_P(tmp = rb_check_string_type(arg))) { arg = tmp; str: - rb_check_safe_obj(arg); ptr = RSTRING_PTR(arg); if (str_to_int(ptr, RSTRING_LEN(arg), &ret) == -1) rb_raise(rb_eSocket, "%s: %s", errmsg, ptr); diff --git a/ext/socket/raddrinfo.c b/ext/socket/raddrinfo.c index 0499b845b5..cc296749b0 100644 --- a/ext/socket/raddrinfo.c +++ b/ext/socket/raddrinfo.c @@ -503,10 +503,6 @@ str_is_number(const char *p) #define str_equal(ptr, len, name) \ ((ptr)[0] == name[0] && \ rb_strlen_lit(name) == (len) && memcmp(ptr, name, len) == 0) -#define SafeStringValueCStr(v) do {\ - StringValueCStr(v);\ - rb_check_safe_obj(v);\ -} while(0) static char* host_str(VALUE host, char *hbuf, size_t hbuflen, int *flags_ptr) @@ -525,7 +521,7 @@ host_str(VALUE host, char *hbuf, size_t hbuflen, int *flags_ptr) const char *name; size_t len; - SafeStringValueCStr(host); + StringValueCStr(host); RSTRING_GETMEM(host, name, len); if (!len || str_equal(name, len, "")) { make_inetaddr(INADDR_ANY, hbuf, hbuflen); @@ -564,7 +560,7 @@ port_str(VALUE port, char *pbuf, size_t pbuflen, int *flags_ptr) const char *serv; size_t len; - SafeStringValueCStr(port); + StringValueCStr(port); RSTRING_GETMEM(port, serv, len); if (len >= pbuflen) { rb_raise(rb_eArgError, "service name too long (%"PRIuSIZE")", diff --git a/ext/socket/unixsocket.c b/ext/socket/unixsocket.c index 8bdfc84575..0c3a01d21e 100644 --- a/ext/socket/unixsocket.c +++ b/ext/socket/unixsocket.c @@ -39,7 +39,6 @@ unixsock_path_value(VALUE path) #endif if (isstr) { if (RSTRING_LEN(name) == 0 || RSTRING_PTR(name)[0] == '\0') { - rb_check_safe_obj(name); return name; /* ignore encoding */ } } diff --git a/ext/syslog/syslog.c b/ext/syslog/syslog.c index 23dcf6c092..4c540fc9c7 100644 --- a/ext/syslog/syslog.c +++ b/ext/syslog/syslog.c @@ -162,7 +162,6 @@ static VALUE mSyslog_open(int argc, VALUE *argv, VALUE self) ident = rb_gv_get("$0"); } ident_ptr = StringValueCStr(ident); - rb_check_safe_obj(ident); syslog_ident = strdup(ident_ptr); if (NIL_P(opt)) { diff --git a/ext/win32ole/win32ole.c b/ext/win32ole/win32ole.c index c46d3937c3..f20bfc84f6 100644 --- a/ext/win32ole/win32ole.c +++ b/ext/win32ole/win32ole.c @@ -1985,10 +1985,6 @@ fole_s_connect(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "1*", &svr_name, &others); StringValue(svr_name); - if (rb_safe_level() > 0 && OBJ_TAINTED(svr_name)) { - rb_raise(rb_eSecurityError, "insecure connection - `%s'", - StringValuePtr(svr_name)); - } /* get CLSID from OLE server name */ pBuf = ole_vstr2wc(svr_name); @@ -2478,16 +2474,8 @@ fole_initialize(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "11*:", &svr_name, &host, &others, &opts); StringValue(svr_name); - if (rb_safe_level() > 0 && OBJ_TAINTED(svr_name)) { - rb_raise(rb_eSecurityError, "insecure object creation - `%s'", - StringValuePtr(svr_name)); - } if (!NIL_P(host)) { StringValue(host); - if (rb_safe_level() > 0 && OBJ_TAINTED(host)) { - rb_raise(rb_eSecurityError, "insecure object creation - `%s'", - StringValuePtr(host)); - } return ole_create_dcom(self, svr_name, host, others); } diff --git a/ext/win32ole/win32ole_event.c b/ext/win32ole/win32ole_event.c index ddb5200b9a..041639af6c 100644 --- a/ext/win32ole/win32ole_event.c +++ b/ext/win32ole/win32ole_event.c @@ -922,10 +922,6 @@ ev_advise(int argc, VALUE *argv, VALUE self) if(!RB_TYPE_P(itf, T_NIL)) { pitf = StringValuePtr(itf); - if (rb_safe_level() > 0 && OBJ_TAINTED(itf)) { - rb_raise(rb_eSecurityError, "insecure event creation - `%s'", - StringValuePtr(itf)); - } hr = find_iid(ole, pitf, &iid, &pTypeInfo); } else { diff --git a/file.c b/file.c index 17881c065d..67ef2d0503 100644 --- a/file.c +++ b/file.c @@ -195,15 +195,11 @@ check_path_encoding(VALUE str) } VALUE -rb_get_path_check_to_string(VALUE obj, int level) +rb_get_path_check_to_string(VALUE obj) { VALUE tmp; ID to_path; - if (insecure_obj_p(obj, level)) { - rb_insecure_operation(); - } - if (RB_TYPE_P(obj, T_STRING)) { return obj; } @@ -214,38 +210,28 @@ rb_get_path_check_to_string(VALUE obj, int level) } VALUE -rb_get_path_check_convert(VALUE obj, VALUE tmp, int level) +rb_get_path_check_convert(VALUE obj) { - tmp = file_path_convert(tmp); - if (obj != tmp && insecure_obj_p(tmp, level)) { - rb_insecure_operation(); - } + obj = file_path_convert(obj); - check_path_encoding(tmp); - if (!rb_str_to_cstr(tmp)) { + check_path_encoding(obj); + if (!rb_str_to_cstr(obj)) { rb_raise(rb_eArgError, "path name contains null byte"); } - return rb_str_new4(tmp); -} - -VALUE -rb_get_path_check(VALUE obj, int level) -{ - VALUE tmp = rb_get_path_check_to_string(obj, level); - return rb_get_path_check_convert(obj, tmp, level); + return rb_str_new4(obj); } VALUE rb_get_path_no_checksafe(VALUE obj) { - return rb_get_path_check(obj, 0); + return rb_get_path(obj); } VALUE rb_get_path(VALUE obj) { - return rb_get_path_check(obj, rb_safe_level()); + return rb_get_path_check_convert(rb_get_path_check_to_string(obj)); } VALUE @@ -6290,13 +6276,14 @@ copy_path_class(VALUE path, VALUE orig) } int -rb_find_file_ext(VALUE *filep, const char *const *ext) +rb_find_file_ext_safe(VALUE *filep, const char *const *ext, int _level) { - return rb_find_file_ext_safe(filep, ext, rb_safe_level()); + rb_warn("rb_find_file_ext_safe will be removed in Ruby 3.0"); + return rb_find_file_ext(filep, ext); } int -rb_find_file_ext_safe(VALUE *filep, const char *const *ext, int safe_level) +rb_find_file_ext(VALUE *filep, const char *const *ext) { const char *f = StringValueCStr(*filep); VALUE fname = *filep, load_path, tmp; @@ -6307,18 +6294,12 @@ rb_find_file_ext_safe(VALUE *filep, const char *const *ext, int safe_level) if (f[0] == '~') { fname = file_expand_path_1(fname); - if (safe_level >= 1 && OBJ_TAINTED(fname)) { - rb_raise(rb_eSecurityError, "loading from unsafe file %s", f); - } f = RSTRING_PTR(fname); *filep = fname; expanded = 1; } if (expanded || rb_is_absolute_path(f) || is_explicit_relative(f)) { - if (safe_level >= 1 && !fpath_check(fname)) { - rb_raise(rb_eSecurityError, "loading from unsafe path %s", f); - } if (!expanded) fname = file_expand_path_1(fname); fnlen = RSTRING_LEN(fname); for (i=0; ext[i]; i++) { @@ -6345,7 +6326,7 @@ rb_find_file_ext_safe(VALUE *filep, const char *const *ext, int safe_level) for (i = 0; i < RARRAY_LEN(load_path); i++) { VALUE str = RARRAY_AREF(load_path, i); - RB_GC_GUARD(str) = rb_get_path_check(str, safe_level); + RB_GC_GUARD(str) = rb_get_path(str); if (RSTRING_LEN(str) == 0) continue; rb_file_expand_path_internal(fname, str, 0, 0, tmp); if (rb_file_load_ok(RSTRING_PTR(tmp))) { @@ -6362,13 +6343,14 @@ rb_find_file_ext_safe(VALUE *filep, const char *const *ext, int safe_level) } VALUE -rb_find_file(VALUE path) +rb_find_file_safe(VALUE path, int _level) { - return rb_find_file_safe(path, rb_safe_level()); + rb_warn("rb_find_file_safe will be removed in Ruby 3.0"); + return rb_find_file(path); } VALUE -rb_find_file_safe(VALUE path, int safe_level) +rb_find_file(VALUE path) { VALUE tmp, load_path; const char *f = StringValueCStr(path); @@ -6376,18 +6358,12 @@ rb_find_file_safe(VALUE path, int safe_level) if (f[0] == '~') { tmp = file_expand_path_1(path); - if (safe_level >= 1 && OBJ_TAINTED(tmp)) { - rb_raise(rb_eSecurityError, "loading from unsafe file %"PRIsVALUE, tmp); - } path = copy_path_class(tmp, path); f = RSTRING_PTR(path); expanded = 1; } if (expanded || rb_is_absolute_path(f) || is_explicit_relative(f)) { - if (safe_level >= 1 && !fpath_check(path)) { - rb_raise(rb_eSecurityError, "loading from unsafe path %"PRIsVALUE, path); - } if (!rb_file_load_ok(f)) return 0; if (!expanded) path = copy_path_class(file_expand_path_1(path), path); @@ -6402,7 +6378,7 @@ rb_find_file_safe(VALUE path, int safe_level) rb_enc_associate_index(tmp, rb_usascii_encindex()); for (i = 0; i < RARRAY_LEN(load_path); i++) { VALUE str = RARRAY_AREF(load_path, i); - RB_GC_GUARD(str) = rb_get_path_check(str, safe_level); + RB_GC_GUARD(str) = rb_get_path(str); if (RSTRING_LEN(str) > 0) { rb_file_expand_path_internal(path, str, 0, 0, tmp); f = RSTRING_PTR(tmp); @@ -6417,10 +6393,6 @@ rb_find_file_safe(VALUE path, int safe_level) } found: - if (safe_level >= 1 && !fpath_check(tmp)) { - rb_raise(rb_eSecurityError, "loading from unsafe file %"PRIsVALUE, tmp); - } - return copy_path_class(tmp, path); } diff --git a/gc.c b/gc.c index 2ef9569013..7f541e99bc 100644 --- a/gc.c +++ b/gc.c @@ -3232,7 +3232,7 @@ define_final0(VALUE obj, VALUE block) RBASIC(obj)->flags |= FL_FINALIZE; - block = rb_ary_new3(2, INT2FIX(rb_safe_level()), block); + block = rb_ary_new3(2, INT2FIX(0), block); OBJ_FREEZE(block); if (st_lookup(finalizer_table, obj, &data)) { @@ -3291,7 +3291,6 @@ run_single_final(VALUE final, VALUE objid) const int level = OBJ_TAINTED(cmd) ? RUBY_SAFE_LEVEL_MAX : FIX2INT(RARRAY_AREF(final, 0)); - rb_set_safe_level_force(level); return rb_check_funcall(cmd, idCall, 1, &objid); } @@ -3305,15 +3304,12 @@ run_finalizer(rb_objspace_t *objspace, VALUE obj, VALUE table) VALUE objid; rb_control_frame_t *cfp; long finished; - int safe; } saved; rb_execution_context_t * volatile ec = GET_EC(); #define RESTORE_FINALIZER() (\ ec->cfp = saved.cfp, \ - rb_set_safe_level_force(saved.safe), \ rb_set_errinfo(saved.errinfo)) - saved.safe = rb_safe_level(); saved.errinfo = rb_errinfo(); saved.objid = rb_obj_id(obj); saved.cfp = ec->cfp; @@ -9389,10 +9385,8 @@ gc_set_initial_pages(void) */ void -ruby_gc_set_params(int safe_level) +ruby_gc_set_params(void) { - if (safe_level > 0) return; - /* RUBY_GC_HEAP_FREE_SLOTS */ if (get_envparam_size("RUBY_GC_HEAP_FREE_SLOTS", &gc_params.heap_free_slots, 0)) { /* ok */ diff --git a/hash.c b/hash.c index 77c83d9ae3..b0746cb4c6 100644 --- a/hash.c +++ b/hash.c @@ -5719,7 +5719,6 @@ env_has_value(VALUE dmy, VALUE obj) obj = rb_check_string_type(obj); if (NIL_P(obj)) return Qnil; - rb_check_safe_obj(obj); env = GET_ENVIRON(environ); while (*env) { char *s = strchr(*env, '='); @@ -5750,7 +5749,6 @@ env_rassoc(VALUE dmy, VALUE obj) obj = rb_check_string_type(obj); if (NIL_P(obj)) return Qnil; - rb_check_safe_obj(obj); env = GET_ENVIRON(environ); while (*env) { char *s = strchr(*env, '='); diff --git a/include/ruby/intern.h b/include/ruby/intern.h index e53b4e607c..e20aa3155d 100644 --- a/include/ruby/intern.h +++ b/include/ruby/intern.h @@ -459,7 +459,8 @@ int rb_provided(const char*); int rb_feature_provided(const char *, const char **); void rb_provide(const char*); VALUE rb_f_require(VALUE, VALUE); -VALUE rb_require_safe(VALUE, int); +VALUE rb_require_safe(VALUE, int); /* Remove in 3.0 */ +VALUE rb_require_string(VALUE); void rb_obj_call_init(VALUE, int, const VALUE*); void rb_obj_call_init_kw(VALUE, int, const VALUE*, int); VALUE rb_class_new_instance(int, const VALUE*, VALUE); @@ -519,8 +520,8 @@ VALUE rb_file_expand_path(VALUE, VALUE); VALUE rb_file_s_absolute_path(int, const VALUE *); VALUE rb_file_absolute_path(VALUE, VALUE); VALUE rb_file_dirname(VALUE fname); -int rb_find_file_ext_safe(VALUE*, const char* const*, int); -VALUE rb_find_file_safe(VALUE, int); +int rb_find_file_ext_safe(VALUE*, const char* const*, int); /* Remove in 3.0 */ +VALUE rb_find_file_safe(VALUE, int); /* Remove in 3.0 */ int rb_find_file_ext(VALUE*, const char* const*); VALUE rb_find_file(VALUE); VALUE rb_file_directory_p(VALUE,VALUE); diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h index 2f9855e41e..8a5aacb42b 100644 --- a/include/ruby/ruby.h +++ b/include/ruby/ruby.h @@ -604,21 +604,18 @@ char *rb_string_value_cstr(volatile VALUE*); #define StringValueCStr(v) rb_string_value_cstr(&(v)) void rb_check_safe_obj(VALUE); -#define SafeStringValue(v) do {\ - StringValue(v);\ - rb_check_safe_obj(v);\ -} while (0) +#define SafeStringValue(v) StringValue(v) #if GCC_VERSION_SINCE(4,4,0) -void rb_check_safe_str(VALUE) __attribute__((error("rb_check_safe_str() and Check_SafeStr() are obsolete; use SafeStringValue() instead"))); +void rb_check_safe_str(VALUE) __attribute__((error("rb_check_safe_str() and Check_SafeStr() are obsolete; use StringValue() instead"))); # define Check_SafeStr(v) rb_check_safe_str((VALUE)(v)) #else -# define rb_check_safe_str(x) [<"rb_check_safe_str() is obsolete; use SafeStringValue() instead">] -# define Check_SafeStr(v) [<"Check_SafeStr() is obsolete; use SafeStringValue() instead">] +# define rb_check_safe_str(x) [<"rb_check_safe_str() is obsolete; use StringValue() instead">] +# define Check_SafeStr(v) [<"Check_SafeStr() is obsolete; use StringValue() instead">] #endif VALUE rb_str_export(VALUE); #define ExportStringValue(v) do {\ - SafeStringValue(v);\ + StringValue(v);\ (v) = rb_str_export(v);\ } while (0) VALUE rb_str_export_locale(VALUE); @@ -627,8 +624,9 @@ VALUE rb_get_path(VALUE); #define FilePathValue(v) (RB_GC_GUARD(v) = rb_get_path(v)) VALUE rb_get_path_no_checksafe(VALUE); -#define FilePathStringValue(v) ((v) = rb_get_path_no_checksafe(v)) +#define FilePathStringValue(v) ((v) = rb_get_path(v)) +/* Remove in 3.0 */ #define RUBY_SAFE_LEVEL_MAX 1 void rb_secure(int); int rb_safe_level(void); diff --git a/internal.h b/internal.h index ec8725aa59..3cfb5a40ec 100644 --- a/internal.h +++ b/internal.h @@ -1570,9 +1570,8 @@ void rb_file_const(const char*, VALUE); int rb_file_load_ok(const char *); VALUE rb_file_expand_path_fast(VALUE, VALUE); VALUE rb_file_expand_path_internal(VALUE, VALUE, int, int, VALUE); -VALUE rb_get_path_check_to_string(VALUE, int); -VALUE rb_get_path_check_convert(VALUE, VALUE, int); -VALUE rb_get_path_check(VALUE, int); +VALUE rb_get_path_check_to_string(VALUE); +VALUE rb_get_path_check_convert(VALUE); void Init_File(void); int ruby_is_fd_loadable(int fd); @@ -1604,7 +1603,7 @@ void rb_gc_writebarrier_remember(VALUE obj); #else #define rb_gc_writebarrier_remember(obj) 0 #endif -void ruby_gc_set_params(int safe_level); +void ruby_gc_set_params(void); void rb_copy_wb_protected_attribute(VALUE dest, VALUE obj); #if defined(HAVE_MALLOC_USABLE_SIZE) || defined(HAVE_MALLOC_SIZE) || defined(_WIN32) @@ -1696,7 +1695,7 @@ void rb_io_fptr_finalize_internal(void *ptr); /* load.c */ VALUE rb_get_expanded_load_path(void); -int rb_require_internal(VALUE fname, int safe); +int rb_require_internal(VALUE fname); NORETURN(void rb_load_fail(VALUE, const char*)); /* loadpath.c */ diff --git a/io.c b/io.c index f4dc499633..47d37d121a 100644 --- a/io.c +++ b/io.c @@ -12839,9 +12839,6 @@ opt_i_get(ID id, VALUE *var) static VALUE argf_inplace_mode_set(VALUE argf, VALUE val) { - if (rb_safe_level() >= 1 && OBJ_TAINTED(val)) - rb_insecure_operation(); - if (!RTEST(val)) { ARGF.inplace = Qfalse; } diff --git a/iseq.c b/iseq.c index c2f241284e..cb179d18b3 100644 --- a/iseq.c +++ b/iseq.c @@ -1175,8 +1175,6 @@ iseqw_s_compile(int argc, VALUE *argv, VALUE self) VALUE src, file = Qnil, path = Qnil, line = INT2FIX(1), opt = Qnil; int i; - rb_secure(1); - i = rb_scan_args(argc, argv, "1*:", &src, NULL, &opt); if (i > 4+NIL_P(opt)) rb_error_arity(argc, 1, 5); switch (i) { @@ -1225,7 +1223,6 @@ iseqw_s_compile_file(int argc, VALUE *argv, VALUE self) rb_compile_option_t option; int i; - rb_secure(1); i = rb_scan_args(argc, argv, "1*:", &file, NULL, &opt); if (i > 1+NIL_P(opt)) rb_error_arity(argc, 1, 2); switch (i) { @@ -1292,7 +1289,6 @@ static VALUE iseqw_s_compile_option_set(VALUE self, VALUE opt) { rb_compile_option_t option; - rb_secure(1); make_compile_option(&option, opt); COMPILE_OPTION_DEFAULT = option; return opt; @@ -1344,7 +1340,6 @@ rb_iseqw_to_iseq(VALUE iseqw) static VALUE iseqw_eval(VALUE self) { - rb_secure(1); return rb_iseq_eval(iseqw_check(self)); } @@ -1579,7 +1574,6 @@ static VALUE iseqw_to_a(VALUE self) { const rb_iseq_t *iseq = iseqw_check(self); - rb_secure(1); return iseq_data_to_ary(iseq); } @@ -2134,8 +2128,6 @@ rb_iseq_disasm_recursive(const rb_iseq_t *iseq, VALUE indent) const char *indent_str; long indent_len; - rb_secure(1); - size = body->iseq_size; indent_len = RSTRING_LEN(indent); @@ -2438,8 +2430,6 @@ iseqw_s_of(VALUE klass, VALUE body) { const rb_iseq_t *iseq = NULL; - rb_secure(1); - if (rb_obj_is_proc(body)) { iseq = vm_proc_iseq(body); diff --git a/lib/debug.rb b/lib/debug.rb index 34d7d27406..ce8b7d7fb9 100644 --- a/lib/debug.rb +++ b/lib/debug.rb @@ -5,11 +5,6 @@ require 'continuation' -if $SAFE > 0 - STDERR.print "-r debug.rb is not available in safe mode\n" - exit 1 -end - require 'tracer' require 'pp' diff --git a/lib/drb/drb.rb b/lib/drb/drb.rb index 5c7f66ac16..0063e20144 100644 --- a/lib/drb/drb.rb +++ b/lib/drb/drb.rb @@ -160,8 +160,6 @@ require_relative 'eq' # # The object that handles requests on the server # FRONT_OBJECT=TimeServer.new # -# $SAFE = 1 # disable eval() and friends -# # DRb.start_service(URI, FRONT_OBJECT) # # Wait for the drb server thread to finish before exiting. # DRb.thread.join @@ -245,8 +243,6 @@ require_relative 'eq' # # FRONT_OBJECT=LoggerFactory.new("/tmp/dlog") # -# $SAFE = 1 # disable eval() and friends -# # DRb.start_service(URI, FRONT_OBJECT) # DRb.thread.join # @@ -286,10 +282,7 @@ require_relative 'eq' # ro.instance_eval("`rm -rf *`") # # The dangers posed by instance_eval and friends are such that a -# DRbServer should generally be run with $SAFE set to at least -# level 1. This will disable eval() and related calls on strings -# passed across the wire. The sample usage code given above follows -# this practice. +# DRbServer should only be used when clients are trusted. # # A DRbServer can be configured with an access control list to # selectively allow or deny access from specified IP addresses. The @@ -1362,7 +1355,6 @@ module DRb @@argc_limit = 256 @@load_limit = 0xffffffff @@verbose = false - @@safe_level = 0 # Set the default value for the :argc_limit option. # @@ -1392,11 +1384,8 @@ module DRb @@idconv = idconv end - # Set the default safe level to +level+. The default safe level is 0 - # - # See #new for more information. - def self.default_safe_level(level) - @@safe_level = level + def self.default_safe_level(level) # :nodoc: + # Remove in Ruby 3.0 end # Set the default value of the :verbose option. @@ -1418,7 +1407,6 @@ module DRb :tcp_acl => @@acl, :load_limit => @@load_limit, :argc_limit => @@argc_limit, - :safe_level => @@safe_level } default_config.update(hash) end @@ -1452,10 +1440,6 @@ module DRb # :argc_limit :: the maximum number of arguments to a remote # method accepted by the server. Defaults to # 256. - # :safe_level :: The safe level of the DRbServer. The attribute - # sets $SAFE for methods performed in the main_loop. - # Defaults to 0. - # # The default values of these options can be modified on # a class-wide basis by the class methods #default_argc_limit, # #default_load_limit, #default_acl, #default_id_conv, @@ -1487,7 +1471,6 @@ module DRb @front = front @idconv = @config[:idconv] - @safe_level = @config[:safe_level] @grp = ThreadGroup.new @thread = run @@ -1514,11 +1497,10 @@ module DRb # The configuration of this DRbServer attr_reader :config - # The safe level for this server. This is a number corresponding to - # $SAFE. - # - # The default safe_level is 0 - attr_reader :safe_level + def safe_level # :nodoc: + # Remove in Ruby 3.0 + 0 + end # Set whether to operate in verbose mode. # @@ -1652,7 +1634,6 @@ module DRb class InvokeMethod # :nodoc: def initialize(drb_server, client) @drb_server = drb_server - @safe_level = drb_server.safe_level @client = client end @@ -1661,33 +1642,10 @@ module DRb @succ = false setup_message - if $SAFE < @safe_level - info = Thread.current['DRb'] - if @block - @result = Thread.new do - Thread.current['DRb'] = info - prev_safe_level = $SAFE - $SAFE = @safe_level - perform_with_block - ensure - $SAFE = prev_safe_level - end.value - else - @result = Thread.new do - Thread.current['DRb'] = info - prev_safe_level = $SAFE - $SAFE = @safe_level - perform_without_block - ensure - $SAFE = prev_safe_level - end.value - end + if @block + @result = perform_with_block else - if @block - @result = perform_with_block - else - @result = perform_without_block - end + @result = perform_without_block end @succ = true case @result diff --git a/lib/erb.rb b/lib/erb.rb index 3f26c2ff30..d2ea64ab60 100644 --- a/lib/erb.rb +++ b/lib/erb.rb @@ -57,7 +57,6 @@ require "cgi/util" # # There are several settings you can change when you use ERB: # * the nature of the tags that are recognized; -# * the value of $SAFE under which the template is run; # * the binding used to resolve local variables in the template. # # See the ERB.new and ERB#result methods for more detail. @@ -747,9 +746,7 @@ class ERB # Constructs a new ERB object with the template specified in _str_. # # An ERB object works by building a chunk of Ruby code that will output - # the completed template when run. If _safe_level_ is set to a non-nil value, - # ERB code will be run in a separate thread with $SAFE set to the - # provided level. + # the completed template when run. # # If _trim_mode_ is passed a String containing one or more of the following # modifiers, ERB will adjust its code generation as listed: @@ -813,8 +810,6 @@ class ERB # Complex initializer for $SAFE deprecation at [Feature #14256]. Use keyword arguments to pass trim_mode or eoutvar. if safe_level != NOT_GIVEN warn 'Passing safe_level with the 2nd argument of ERB.new is deprecated. Do not use it, and specify other arguments as keyword arguments.', uplevel: 1 if $VERBOSE || !ZERO_SAFE_LEVELS.include?(safe_level) - else - safe_level = nil end if legacy_trim_mode != NOT_GIVEN warn 'Passing trim_mode with the 3rd argument of ERB.new is deprecated. Use keyword argument like ERB.new(str, trim_mode: ...) instead.', uplevel: 1 if $VERBOSE @@ -825,7 +820,6 @@ class ERB eoutvar = legacy_eoutvar end - @safe_level = safe_level compiler = make_compiler(trim_mode) set_eoutvar(compiler, eoutvar) @src, @encoding, @frozen_string = *compiler.compile(str) @@ -908,17 +902,7 @@ class ERB unless @_init.equal?(self.class.singleton_class) raise ArgumentError, "not initialized" end - if @safe_level - proc do - prev_safe_level = $SAFE - $SAFE = @safe_level - eval(@src, b, (@filename || '(erb)'), @lineno) - ensure - $SAFE = prev_safe_level - end.call - else - eval(@src, b, (@filename || '(erb)'), @lineno) - end + eval(@src, b, (@filename || '(erb)'), @lineno) end # Render a template on a new toplevel binding with local variables specified diff --git a/lib/net/smtp.rb b/lib/net/smtp.rb index 8e10dc24b3..460ad08233 100644 --- a/lib/net/smtp.rb +++ b/lib/net/smtp.rb @@ -831,9 +831,6 @@ module Net end def mailfrom(from_addr) - if $SAFE > 0 - raise SecurityError, 'tainted from_addr' if from_addr.tainted? - end getok("MAIL FROM:<#{from_addr}>") end @@ -859,9 +856,6 @@ module Net end def rcptto(to_addr) - if $SAFE > 0 - raise SecurityError, 'tainted to_addr' if to_addr.tainted? - end getok("RCPT TO:<#{to_addr}>") end diff --git a/lib/tempfile.rb b/lib/tempfile.rb index f7caf65f0c..efb0b1bcd6 100644 --- a/lib/tempfile.rb +++ b/lib/tempfile.rb @@ -98,10 +98,6 @@ class Tempfile < DelegateClass(File) # # The temporary file will be placed in the directory as specified # by the +tmpdir+ parameter. By default, this is +Dir.tmpdir+. - # When $SAFE > 0 and the given +tmpdir+ is tainted, it uses - # '/tmp' as the temporary directory. Please note that ENV values - # are tainted by default, and +Dir.tmpdir+'s return value might - # come from environment variables (e.g. $TMPDIR). # # file = Tempfile.new('hello', '/home/aisaka') # file.path # => something like: "/home/aisaka/hello2843-8392-92849382--0" diff --git a/lib/tmpdir.rb b/lib/tmpdir.rb index d7a8f799fe..ea1d380ef1 100644 --- a/lib/tmpdir.rb +++ b/lib/tmpdir.rb @@ -19,22 +19,18 @@ class Dir # Returns the operating system's temporary file path. def self.tmpdir - if $SAFE > 0 - @@systmpdir.dup - else - tmp = nil - [ENV['TMPDIR'], ENV['TMP'], ENV['TEMP'], @@systmpdir, '/tmp', '.'].each do |dir| - next if !dir - dir = File.expand_path(dir) - if stat = File.stat(dir) and stat.directory? and stat.writable? and - (!stat.world_writable? or stat.sticky?) - tmp = dir - break - end rescue nil - end - raise ArgumentError, "could not find a temporary directory" unless tmp - tmp + tmp = nil + [ENV['TMPDIR'], ENV['TMP'], ENV['TEMP'], @@systmpdir, '/tmp', '.'].each do |dir| + next if !dir + dir = File.expand_path(dir) + if stat = File.stat(dir) and stat.directory? and stat.writable? and + (!stat.world_writable? or stat.sticky?) + tmp = dir + break + end rescue nil end + raise ArgumentError, "could not find a temporary directory" unless tmp + tmp end # Dir.mktmpdir creates a temporary directory. @@ -115,12 +111,8 @@ class Dir UNUSABLE_CHARS = [File::SEPARATOR, File::ALT_SEPARATOR, File::PATH_SEPARATOR, ":"].uniq.join("").freeze def create(basename, tmpdir=nil, max_try: nil, **opts) - if $SAFE > 0 and tmpdir.tainted? - tmpdir = '/tmp' - else - origdir = tmpdir - tmpdir ||= tmpdir() - end + origdir = tmpdir + tmpdir ||= tmpdir() n = nil prefix, suffix = basename prefix = (String.try_convert(prefix) or 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 diff --git a/parse.y b/parse.y index 9eb7bedf34..9277897f83 100644 --- a/parse.y +++ b/parse.y @@ -5902,7 +5902,7 @@ yycompile0(VALUE arg) struct parser_params *p = (struct parser_params *)arg; VALUE cov = Qfalse; - if (!compile_for_eval && rb_safe_level() == 0 && !NIL_P(p->ruby_sourcefile_string)) { + if (!compile_for_eval && !NIL_P(p->ruby_sourcefile_string)) { p->debug_lines = debug_lines(p->ruby_sourcefile_string); if (p->debug_lines && p->ruby_sourceline > 0) { VALUE str = STR_NEW0(); diff --git a/proc.c b/proc.c index 762f704345..607ccc33b0 100644 --- a/proc.c +++ b/proc.c @@ -2275,27 +2275,6 @@ call_method_data(rb_execution_context_t *ec, const struct METHOD *data, method_callable_method_entry(data), kw_splat); } -static VALUE -call_method_data_safe(rb_execution_context_t *ec, const struct METHOD *data, - int argc, const VALUE *argv, VALUE passed_procval, - int safe, int kw_splat) -{ - VALUE result = Qnil; /* OK */ - enum ruby_tag_type state; - - EC_PUSH_TAG(ec); - if ((state = EC_EXEC_TAG()) == TAG_NONE) { - /* result is used only if state == 0, no exceptions is caught. */ - /* otherwise it doesn't matter even if clobbered. */ - NO_CLOBBERED(result) = call_method_data(ec, data, argc, argv, passed_procval, kw_splat); - } - EC_POP_TAG(); - rb_set_safe_level_force(safe); - if (state) - EC_JUMP_TAG(ec, state); - return result; -} - VALUE rb_method_call_with_block_kw(int argc, const VALUE *argv, VALUE method, VALUE passed_procval, int kw_splat) { @@ -2306,14 +2285,6 @@ rb_method_call_with_block_kw(int argc, const VALUE *argv, VALUE method, VALUE pa if (data->recv == Qundef) { rb_raise(rb_eTypeError, "can't call unbound method; bind first"); } - if (OBJ_TAINTED(method)) { - const int safe_level_to_run = RUBY_SAFE_LEVEL_MAX; - int safe = rb_safe_level(); - if (safe < safe_level_to_run) { - rb_set_safe_level_force(safe_level_to_run); - return call_method_data_safe(ec, data, argc, argv, passed_procval, safe, kw_splat); - } - } return call_method_data(ec, data, argc, argv, passed_procval, kw_splat); } diff --git a/process.c b/process.c index 6fa3270e5c..69d362b4d3 100644 --- a/process.c +++ b/process.c @@ -1574,16 +1574,6 @@ after_fork_ruby(void) #include "dln.h" -static void -security(const char *str) -{ - if (rb_env_path_tainted()) { - if (rb_safe_level() > 0) { - rb_raise(rb_eSecurityError, "Insecure PATH - %s", str); - } - } -} - #if defined(HAVE_WORKING_FORK) /* try_with_sh and exec_with_sh should be async-signal-safe. Actually it is.*/ @@ -1759,7 +1749,6 @@ proc_spawn_cmd_internal(char **argv, char *prog) if (!prog) prog = argv[0]; - security(prog); prog = dln_find_exe_r(prog, 0, fbuf, sizeof(fbuf)); if (!prog) return -1; @@ -2371,7 +2360,6 @@ rb_check_argv(int argc, VALUE *argv) argv[i] = rb_str_new_frozen(argv[i]); StringValueCStr(argv[i]); } - security(name ? name : RSTRING_PTR(argv[0])); return prog; } diff --git a/ruby.c b/ruby.c index 02d90282b6..8df0fcfec3 100644 --- a/ruby.c +++ b/ruby.c @@ -162,7 +162,6 @@ struct ruby_cmdline_options { #if USE_MJIT struct mjit_options mjit; #endif - int safe_level; int sflag, xflag; unsigned int warning: 1; unsigned int verbose: 1; @@ -264,7 +263,6 @@ usage(const char *name, int help) M("-rlibrary", "", "require the library before executing your script"), M("-s", "", "enable some switch parsing for switches after script name"), M("-S", "", "look for the script using PATH environment variable"), - M("-T[level=1]", "", "turn on tainting checks"), M("-v", "", "print the version number, then turn on verbose mode"), M("-w", "", "turn warnings on for your script"), M("-W[level=2]", "", "set warning level; 0=silence, 1=medium, 2=verbose"), @@ -490,13 +488,7 @@ str_conv_enc(VALUE str, rb_encoding *from, rb_encoding *to) # define str_conv_enc(str, from, to) (str) #endif -void ruby_init_loadpath_safe(int safe_level); - -void -ruby_init_loadpath(void) -{ - ruby_init_loadpath_safe(0); -} +void ruby_init_loadpath(void); #if defined(LOAD_RELATIVE) static VALUE @@ -576,7 +568,7 @@ runtime_libruby_path(void) VALUE ruby_archlibdir_path, ruby_prefix_path; void -ruby_init_loadpath_safe(int safe_level) +ruby_init_loadpath(void) { VALUE load_path, archlibdir = 0; ID id_initial_load_path_mark; @@ -659,9 +651,7 @@ ruby_init_loadpath_safe(int safe_level) load_path = GET_VM()->load_path; - if (safe_level == 0) { - ruby_push_include(getenv("RUBYLIB"), identical_path); - } + ruby_push_include(getenv("RUBYLIB"), identical_path); id_initial_load_path_mark = INITIAL_LOAD_PATH_MARK; while (*paths) { @@ -1225,18 +1215,18 @@ proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt) goto reswitch; case 'T': - { - size_t numlen; - int v = 1; - - if (*++s) { - v = scan_oct(s, 2, &numlen); - if (numlen == 0) - v = 1; - s += numlen; - } - if (v > opt->safe_level) opt->safe_level = v; - } + { + size_t numlen; + int v = 1; + + if (*++s) { + v = scan_oct(s, 2, &numlen); + if (numlen == 0) + v = 1; + s += numlen; + } + } + rb_warn("ruby -T will be removed in Ruby 3.0"); goto reswitch; case 'I': @@ -1576,8 +1566,7 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt) argc -= i; argv += i; - if ((opt->features.set & FEATURE_BIT(rubyopt)) && - opt->safe_level == 0 && (s = getenv("RUBYOPT"))) { + if ((opt->features.set & FEATURE_BIT(rubyopt)) && (s = getenv("RUBYOPT"))) { VALUE src_enc_name = opt->src.enc.name; VALUE ext_enc_name = opt->ext.enc.name; VALUE int_enc_name = opt->intern.enc.name; @@ -1655,12 +1644,12 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt) translit_char(RSTRING_PTR(opt->script_name), '\\', '/'); #endif - ruby_gc_set_params(opt->safe_level); - ruby_init_loadpath_safe(opt->safe_level); + ruby_gc_set_params(); + ruby_init_loadpath(); #if USE_MJIT if (opt->mjit.on) - /* Using TMP_RUBY_PREFIX created by ruby_init_loadpath_safe(). */ + /* Using TMP_RUBY_PREFIX created by ruby_init_loadpath(). */ mjit_init(&opt->mjit); #endif @@ -1884,8 +1873,6 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt) rb_gc_register_mark_object(opt->e_script); } - rb_set_safe_level(opt->safe_level); - { rb_execution_context_t *ec = GET_EC(); @@ -2282,9 +2269,6 @@ init_ids(ruby_cmdline_options_t *opt) if (uid != euid) opt->setids |= 1; if (egid != gid) opt->setids |= 2; - if (uid && opt->setids) { - if (opt->safe_level < 1) opt->safe_level = 1; - } } #undef forbid_setid @@ -2295,8 +2279,6 @@ forbid_setid(const char *s, const ruby_cmdline_options_t *opt) rb_raise(rb_eSecurityError, "no %s allowed while running setuid", s); if (opt->setids & 2) rb_raise(rb_eSecurityError, "no %s allowed while running setgid", s); - if (opt->safe_level > 0) - rb_raise(rb_eSecurityError, "no %s allowed in tainted mode", s); } static void diff --git a/safe.c b/safe.c index 9c668e3842..7f340ffae2 100644 --- a/safe.c +++ b/safe.c @@ -28,18 +28,21 @@ int ruby_safe_level_2_warning(void) { + rb_warn("rb_safe_level_2_warning will be removed in Ruby 3.0"); return 2; } int rb_safe_level(void) { + rb_warn("rb_safe_level will be removed in Ruby 3.0"); return GET_VM()->safe_level_; } void rb_set_safe_level_force(int safe) { + rb_warn("rb_set_safe_level_force will be removed in Ruby 3.0"); GET_VM()->safe_level_ = safe; } @@ -48,6 +51,7 @@ rb_set_safe_level(int level) { rb_vm_t *vm = GET_VM(); + rb_warn("rb_set_safe_level will be removed in Ruby 3.0"); if (level > SAFE_LEVEL_MAX) { rb_raise(rb_eArgError, "$SAFE=2 to 4 are obsolete"); } @@ -68,28 +72,47 @@ rb_set_safe_level(int level) static VALUE safe_getter(ID _x, VALUE *_y) { - return INT2NUM(rb_safe_level()); + rb_warn("$SAFE will become a normal global variable in Ruby 3.0"); + return INT2NUM(GET_VM()->safe_level_); } static void safe_setter(VALUE val, ID _x, VALUE *_y) { int level = NUM2INT(val); - rb_set_safe_level(level); + rb_vm_t *vm = GET_VM(); + + rb_warn("$SAFE will become a normal global variable in Ruby 3.0"); + if (level > SAFE_LEVEL_MAX) { + rb_raise(rb_eArgError, "$SAFE=2 to 4 are obsolete"); + } + else if (level < 0) { + rb_raise(rb_eArgError, "$SAFE should be >= 0"); + } + else { + int line; + const char *path = rb_source_location_cstr(&line); + + if (0) fprintf(stderr, "%s:%d $SAFE %d -> %d\n", + path ? path : "-", line, vm->safe_level_, level); + + vm->safe_level_ = level; + } } void rb_secure(int level) { - if (level <= rb_safe_level()) { + rb_warn("rb_secure will be removed in Ruby 3.0"); + if (level <= GET_VM()->safe_level_) { ID caller_name = rb_frame_callee(); if (caller_name) { rb_raise(rb_eSecurityError, "Insecure operation `%"PRIsVALUE"' at level %d", - rb_id2str(caller_name), rb_safe_level()); + rb_id2str(caller_name), GET_VM()->safe_level_); } else { rb_raise(rb_eSecurityError, "Insecure operation at level %d", - rb_safe_level()); + GET_VM()->safe_level_); } } } @@ -97,11 +120,13 @@ rb_secure(int level) void rb_secure_update(VALUE obj) { + rb_warn("rb_secure_update will be removed in Ruby 3.0"); } void rb_insecure_operation(void) { + rb_warn("rb_insecure_operation will be removed in Ruby 3.0"); ID caller_name = rb_frame_callee(); if (caller_name) { rb_raise(rb_eSecurityError, "Insecure operation - %"PRIsVALUE, @@ -115,6 +140,7 @@ rb_insecure_operation(void) void rb_check_safe_obj(VALUE x) { + rb_warn("rb_check_safe_obj will be removed in Ruby 3.0"); if (rb_safe_level() > 0 && OBJ_TAINTED(x)) { rb_insecure_operation(); } diff --git a/signal.c b/signal.c index 8fb43067cb..82aeb81718 100644 --- a/signal.c +++ b/signal.c @@ -1024,7 +1024,7 @@ sig_do_nothing(int sig) #endif static int -signal_exec(VALUE cmd, int safe, int sig) +signal_exec(VALUE cmd, int sig) { rb_execution_context_t *ec = GET_EC(); volatile rb_atomic_t old_interrupt_mask = ec->interrupt_mask; @@ -1043,7 +1043,7 @@ signal_exec(VALUE cmd, int safe, int sig) EC_PUSH_TAG(ec); if ((state = EC_EXEC_TAG()) == TAG_NONE) { VALUE signum = INT2NUM(sig); - rb_eval_cmd(cmd, rb_ary_new3(1, signum), safe); + rb_eval_cmd(cmd, rb_ary_new3(1, signum), 0); } EC_POP_TAG(); ec = GET_EC(); @@ -1063,7 +1063,7 @@ rb_vm_trap_exit(rb_vm_t *vm) if (trap_exit) { vm->trap_list.cmd[0] = 0; - signal_exec(trap_exit, vm->trap_list.safe[0], 0); + signal_exec(trap_exit, 0); } } @@ -1083,7 +1083,6 @@ rb_signal_exec(rb_thread_t *th, int sig) { rb_vm_t *vm = GET_VM(); VALUE cmd = vm->trap_list.cmd[sig]; - int safe = vm->trap_list.safe[sig]; if (cmd == 0) { switch (sig) { @@ -1116,7 +1115,7 @@ rb_signal_exec(rb_thread_t *th, int sig) rb_threadptr_signal_exit(th); } else { - return signal_exec(cmd, safe, sig); + return signal_exec(cmd, sig); } return FALSE; } @@ -1302,7 +1301,6 @@ trap(int sig, sighandler_t func, VALUE command) } ACCESS_ONCE(VALUE, vm->trap_list.cmd[sig]) = command; - vm->trap_list.safe[sig] = rb_safe_level(); return oldcmd; } diff --git a/spec/ruby/language/predefined_spec.rb b/spec/ruby/language/predefined_spec.rb index 90ea8c6bb1..cec6bc852c 100644 --- a/spec/ruby/language/predefined_spec.rb +++ b/spec/ruby/language/predefined_spec.rb @@ -771,8 +771,6 @@ __LINE__ String The current line number in the source file. [r/ $LOAD_PATH Array A synonym for $:. [r/o] $-p Object Set to true if the -p option (which puts an implicit while gets . . . end loop around your program) is present on the command line. [r/o] -$SAFE Fixnum The current safe level. This variable’s value may never be - reduced by assignment. [thread] (Not implemented in Rubinius) $VERBOSE Object Set to true if the -v, --version, -W, or -w option is specified on the com- mand line. Set to false if no option, or -W1 is given. Set to nil if -W0 was specified. Setting this option to true causes the interpreter and some diff --git a/spec/ruby/language/safe_spec.rb b/spec/ruby/language/safe_spec.rb index be150df9d1..53ab4f9561 100644 --- a/spec/ruby/language/safe_spec.rb +++ b/spec/ruby/language/safe_spec.rb @@ -1,137 +1,138 @@ require_relative '../spec_helper' describe "The $SAFE variable" do - - ruby_version_is "2.6" do - after :each do - $SAFE = 0 + ruby_version_is ""..."2.7" do + ruby_version_is "2.6" do + after :each do + $SAFE = 0 + end end - end - it "is 0 by default" do - $SAFE.should == 0 - proc { + it "is 0 by default" do $SAFE.should == 0 - }.call - end + proc { + $SAFE.should == 0 + }.call + end - it "can be set to 0" do - proc { - $SAFE = 0 - $SAFE.should == 0 - }.call - end + it "can be set to 0" do + proc { + $SAFE = 0 + $SAFE.should == 0 + }.call + end - it "can be set to 1" do - proc { - $SAFE = 1 - $SAFE.should == 1 - }.call - end + it "can be set to 1" do + proc { + $SAFE = 1 + $SAFE.should == 1 + }.call + end - [2, 3, 4].each do |n| - it "cannot be set to #{n}" do + [2, 3, 4].each do |n| + it "cannot be set to #{n}" do + -> { + proc { + $SAFE = n + }.call + }.should raise_error(ArgumentError, /\$SAFE=2 to 4 are obsolete/) + end + end + + ruby_version_is ""..."2.6" do + it "cannot be set to values below 0" do + -> { + proc { + $SAFE = -100 + }.call + }.should raise_error(SecurityError, /tried to downgrade safe level from 0 to -100/) + end + end + + ruby_version_is "2.6" do + it "raises ArgumentError when set to values below 0" do + -> { + proc { + $SAFE = -100 + }.call + }.should raise_error(ArgumentError, "$SAFE should be >= 0") + end + end + + it "cannot be set to values above 4" do -> { proc { - $SAFE = n + $SAFE = 100 }.call }.should raise_error(ArgumentError, /\$SAFE=2 to 4 are obsolete/) end - end - ruby_version_is ""..."2.6" do - it "cannot be set to values below 0" do - -> { + ruby_version_is ""..."2.6" do + it "cannot be manually lowered" do proc { - $SAFE = -100 + $SAFE = 1 + -> { + $SAFE = 0 + }.should raise_error(SecurityError, /tried to downgrade safe level from 1 to 0/) }.call - }.should raise_error(SecurityError, /tried to downgrade safe level from 0 to -100/) - end - end + end - ruby_version_is "2.6" do - it "raises ArgumentError when set to values below 0" do - -> { + it "is automatically lowered when leaving a proc" do + $SAFE.should == 0 proc { - $SAFE = -100 + $SAFE = 1 }.call - }.should raise_error(ArgumentError, "$SAFE should be >= 0") - end - end - - it "cannot be set to values above 4" do - -> { - proc { - $SAFE = 100 - }.call - }.should raise_error(ArgumentError, /\$SAFE=2 to 4 are obsolete/) - end + $SAFE.should == 0 + end - ruby_version_is ""..."2.6" do - it "cannot be manually lowered" do - proc { - $SAFE = 1 + it "is automatically lowered when leaving a lambda" do + $SAFE.should == 0 -> { - $SAFE = 0 - }.should raise_error(SecurityError, /tried to downgrade safe level from 1 to 0/) - }.call + $SAFE = 1 + }.call + $SAFE.should == 0 + end end - it "is automatically lowered when leaving a proc" do - $SAFE.should == 0 - proc { + ruby_version_is "2.6" do + it "can be manually lowered" do $SAFE = 1 - }.call - $SAFE.should == 0 - end + $SAFE = 0 + $SAFE.should == 0 + end - it "is automatically lowered when leaving a lambda" do - $SAFE.should == 0 - -> { + it "is not Proc local" do + $SAFE.should == 0 + proc { + $SAFE = 1 + }.call + $SAFE.should == 1 + end + + it "is not lambda local" do + $SAFE.should == 0 + -> { + $SAFE = 1 + }.call + $SAFE.should == 1 + end + + it "is global like regular global variables" do + Thread.new { $SAFE }.value.should == 0 $SAFE = 1 - }.call - $SAFE.should == 0 + Thread.new { $SAFE }.value.should == 1 + end end - end - ruby_version_is "2.6" do - it "can be manually lowered" do - $SAFE = 1 - $SAFE = 0 - $SAFE.should == 0 + it "can be read when default from Thread#safe_level" do + Thread.current.safe_level.should == 0 end - it "is not Proc local" do - $SAFE.should == 0 + it "can be read when modified from Thread#safe_level" do proc { $SAFE = 1 + Thread.current.safe_level.should == 1 }.call - $SAFE.should == 1 end - - it "is not lambda local" do - $SAFE.should == 0 - -> { - $SAFE = 1 - }.call - $SAFE.should == 1 - end - - it "is global like regular global variables" do - Thread.new { $SAFE }.value.should == 0 - $SAFE = 1 - Thread.new { $SAFE }.value.should == 1 - end - end - - it "can be read when default from Thread#safe_level" do - Thread.current.safe_level.should == 0 - end - - it "can be read when modified from Thread#safe_level" do - proc { - $SAFE = 1 - Thread.current.safe_level.should == 1 - }.call end end diff --git a/spec/ruby/optional/capi/string_spec.rb b/spec/ruby/optional/capi/string_spec.rb index 1cfd590ca4..53d28f7940 100644 --- a/spec/ruby/optional/capi/string_spec.rb +++ b/spec/ruby/optional/capi/string_spec.rb @@ -558,20 +558,22 @@ describe "C-API String function" do end describe "SafeStringValue" do - it "raises for tained string when $SAFE is 1" do - begin - Thread.new { - $SAFE = 1 - -> { - @s.SafeStringValue("str".taint) - }.should raise_error(SecurityError) - }.join - ensure - $SAFE = 0 + ruby_version_is ''...'2.7' do + it "raises for tained string when $SAFE is 1" do + begin + Thread.new { + $SAFE = 1 + -> { + @s.SafeStringValue("str".taint) + }.should raise_error(SecurityError) + }.join + ensure + $SAFE = 0 + end end - end - it_behaves_like :string_value_macro, :SafeStringValue + it_behaves_like :string_value_macro, :SafeStringValue + end end describe "rb_str_modify_expand" do diff --git a/test/net/imap/test_imap_response_parser.rb b/test/net/imap/test_imap_response_parser.rb index ed31a03f5a..5c2d54566d 100644 --- a/test/net/imap/test_imap_response_parser.rb +++ b/test/net/imap/test_imap_response_parser.rb @@ -20,19 +20,6 @@ class IMAPResponseParserTest < Test::Unit::TestCase end end - def test_flag_list_safe - parser = Net::IMAP::ResponseParser.new - response = lambda { - $SAFE = 1 - parser.parse(< ", stdout.read(2)) assert_equal(1, Readline::HISTORY.length) assert_equal("hello", Readline::HISTORY[0]) - - # Work around lack of SecurityError in Reline - # test mode with tainted prompt - return if kind_of?(TestRelineAsReadline) - - Thread.start { - $SAFE = 1 - assert_raise(SecurityError) do - replace_stdio(stdin.path, stdout.path) do - Readline.readline("> ".taint) - end - end - }.join - ensure - $SAFE = 0 end end diff --git a/test/ruby/test_alias.rb b/test/ruby/test_alias.rb index e81636fa43..33fb82e1d7 100644 --- a/test/ruby/test_alias.rb +++ b/test/ruby/test_alias.rb @@ -47,12 +47,6 @@ class TestAlias < Test::Unit::TestCase assert_raise(NoMethodError) { x.quux } end - class C - def m - $SAFE - end - end - def test_nonexistmethod assert_raise(NameError){ Class.new{ diff --git a/test/ruby/test_file.rb b/test/ruby/test_file.rb index f984a8fd23..5599040e1e 100644 --- a/test/ruby/test_file.rb +++ b/test/ruby/test_file.rb @@ -471,18 +471,6 @@ class TestFile < Test::Unit::TestCase end end - def test_untainted_path - bug5374 = '[ruby-core:39745]' - cwd = ("./"*40+".".taint).dup.untaint - in_safe = proc {|safe| $SAFE = safe; File.stat(cwd)} - assert_not_send([cwd, :tainted?]) - (0..1).each do |level| - assert_nothing_raised(SecurityError, bug5374) {in_safe[level]} - end - ensure - $SAFE = 0 - end - if /(bcc|ms|cyg)win|mingw|emx/ =~ RUBY_PLATFORM def test_long_unc feature3399 = '[ruby-core:30623]' diff --git a/test/ruby/test_optimization.rb b/test/ruby/test_optimization.rb index f26b31f115..b42314b765 100644 --- a/test/ruby/test_optimization.rb +++ b/test/ruby/test_optimization.rb @@ -714,17 +714,6 @@ class TestRubyOptimization < Test::Unit::TestCase END end - def test_block_parameter_should_restore_safe_level - assert_separately [], <<-END - # - def foo &b - $SAFE = 1 - b.call - end - assert_equal 1, foo{$SAFE} - END - end - def test_peephole_optimization_without_trace assert_separately [], <<-END RubyVM::InstructionSequence.compile_option = {trace_instruction: false} diff --git a/test/ruby/test_proc.rb b/test/ruby/test_proc.rb index 5c12043873..0e0b5c7b8b 100644 --- a/test/ruby/test_proc.rb +++ b/test/ruby/test_proc.rb @@ -157,45 +157,6 @@ class TestProc < Test::Unit::TestCase assert_equal(12, Proc.new{|a,&b| b.call(a)}.call(12) {|x| x}) end - def test_safe - safe = $SAFE - c = Class.new - x = c.new - - p = proc { - $SAFE += 1 - proc {$SAFE} - }.call - - assert_equal(safe + 1, $SAFE) - assert_equal(safe + 1, p.call) - assert_equal(safe + 1, $SAFE) - - $SAFE = 0 - c.class_eval {define_method(:safe, p)} - assert_equal(safe, x.safe) - - $SAFE = 0 - p = proc {$SAFE += 1} - assert_equal(safe + 1, p.call) - assert_equal(safe + 1, $SAFE) - - $SAFE = 0 - c.class_eval {define_method(:inc, p)} - assert_equal(safe + 1, proc {x.inc; $SAFE}.call) - assert_equal(safe + 1, $SAFE) - - $SAFE = 0 - assert_equal(safe + 1, proc {x.method(:inc).call; $SAFE}.call) - assert_equal(safe + 1, $SAFE) - - $SAFE = 0 - assert_equal(safe + 1, proc {x.method(:inc).to_proc.call; $SAFE}.call) - assert_equal(safe + 1, $SAFE) - ensure - $SAFE = 0 - end - def m2 "OK" end diff --git a/test/ruby/test_require.rb b/test/ruby/test_require.rb index 560ce3ff28..e21ed88e47 100644 --- a/test/ruby/test_require.rb +++ b/test/ruby/test_require.rb @@ -396,13 +396,6 @@ class TestRequire < Test::Unit::TestCase assert_nothing_raised {require "#{ file }"} INPUT - assert_separately([], <<-INPUT) - abs_dir = "#{ abs_dir }" - $: << abs_dir.taint - $SAFE = 1 - assert_raise(SecurityError) {require "#{ file }"} - INPUT - assert_separately([], <<-INPUT) abs_dir = "#{ abs_dir }" $: << abs_dir << 'elsewhere'.taint diff --git a/test/ruby/test_rubyoptions.rb b/test/ruby/test_rubyoptions.rb index 22ea6b5293..27a9434a5c 100644 --- a/test/ruby/test_rubyoptions.rb +++ b/test/ruby/test_rubyoptions.rb @@ -79,14 +79,6 @@ class TestRubyOptions < Test::Unit::TestCase ENV['RUBYOPT'] = save_rubyopt end - def test_safe_level - assert_in_out_err(%w(-T -e) + [""], "", [], - /no -e allowed in tainted mode \(SecurityError\)/) - - assert_in_out_err(%w(-T4 -S foo.rb), "", [], - /no -S allowed in tainted mode \(SecurityError\)/) - end - def test_debug assert_in_out_err(["--disable-gems", "-de", "p $DEBUG"], "", %w(true), []) @@ -326,12 +318,6 @@ class TestRubyOptions < Test::Unit::TestCase ENV['RUBYOPT'] = '-e "p 1"' assert_in_out_err([], "", [], /invalid switch in RUBYOPT: -e \(RuntimeError\)/) - ENV['RUBYOPT'] = '-T1' - assert_in_out_err(["--disable-gems"], "", [], /no program input from stdin allowed in tainted mode \(SecurityError\)/) - - ENV['RUBYOPT'] = '-T4' - assert_in_out_err(["--disable-gems"], "", [], /no program input from stdin allowed in tainted mode \(SecurityError\)/) - ENV['RUBYOPT'] = '-Eus-ascii -KN' assert_in_out_err(%w(-Eutf-8 -KU), "p '\u3042'") do |r, e| assert_equal("\"\u3042\"", r.join.force_encoding(Encoding::UTF_8)) diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb index e0efb7b2e0..adfad7e7e8 100644 --- a/test/ruby/test_thread.rb +++ b/test/ruby/test_thread.rb @@ -533,23 +533,6 @@ class TestThread < Test::Unit::TestCase waiter&.kill&.join end - def test_safe_level - ok = false - t = Thread.new do - EnvUtil.suppress_warning do - $SAFE = 1 - end - ok = true - sleep - end - Thread.pass until ok - assert_equal($SAFE, Thread.current.safe_level) - assert_equal($SAFE, t.safe_level) - ensure - $SAFE = 0 - t&.kill&.join - end - def test_thread_local t = Thread.new { sleep } diff --git a/test/test_tempfile.rb b/test/test_tempfile.rb index 7c911a1bf7..6b087f9207 100644 --- a/test/test_tempfile.rb +++ b/test/test_tempfile.rb @@ -31,17 +31,6 @@ class TestTempfile < Test::Unit::TestCase assert_equal "hello world", File.read(path) end - def test_saves_in_dir_tmpdir_by_default - t = tempfile("foo") - assert_equal Dir.tmpdir, File.dirname(t.path) - bug3733 = '[ruby-dev:42089]' - assert_nothing_raised(SecurityError, bug3733) { - proc {$SAFE = 1; File.expand_path(Dir.tmpdir)}.call - } - ensure - $SAFE = 0 - end - def test_saves_in_given_directory subdir = File.join(Dir.tmpdir, "tempfile-test-#{rand}") Dir.mkdir(subdir) diff --git a/test/test_tmpdir.rb b/test/test_tmpdir.rb index 42bcbc00a8..c599dcf516 100644 --- a/test/test_tmpdir.rb +++ b/test/test_tmpdir.rb @@ -11,19 +11,6 @@ class TestTmpdir < Test::Unit::TestCase assert_equal(tmpdir_org, Dir.tmpdir) end - def test_tmpdir_modifiable_safe - Thread.new { - $SAFE = 1 - tmpdir = Dir.tmpdir - assert_equal(false, tmpdir.frozen?) - tmpdir_org = tmpdir.dup - tmpdir << "foo" - assert_equal(tmpdir_org, Dir.tmpdir) - }.join - ensure - $SAFE = 0 - end - def test_world_writable skip "no meaning on this platform" if /mswin|mingw/ =~ RUBY_PLATFORM Dir.mktmpdir do |tmpdir| diff --git a/test/win32ole/test_win32ole.rb b/test/win32ole/test_win32ole.rb index 7dda36ce96..3941780528 100644 --- a/test/win32ole/test_win32ole.rb +++ b/test/win32ole/test_win32ole.rb @@ -176,39 +176,6 @@ if defined?(WIN32OLE) } end - def test_s_new_exc_svr_tainted - th = Thread.start { - $SAFE = 1 - svr = "Scripting.Dictionary" - svr.taint - Thread.current.report_on_exception = false - WIN32OLE.new(svr) - } - exc = assert_raise(SecurityError) { - th.join - } - assert_match(/insecure object creation - `Scripting.Dictionary'/, exc.message) - ensure - $SAFE = 0 - end - - def test_s_new_exc_host_tainted - th = Thread.start { - $SAFE = 1 - svr = "Scripting.Dictionary" - host = "localhost" - host.taint - Thread.current.report_on_exception = false - WIN32OLE.new(svr, host) - } - exc = assert_raise(SecurityError) { - th.join - } - assert_match(/insecure object creation - `localhost'/, exc.message) - ensure - $SAFE = 0 - end - def test_s_new_DCOM rshell = WIN32OLE.new("Shell.Application") assert_instance_of(WIN32OLE, rshell) @@ -234,22 +201,6 @@ if defined?(WIN32OLE) } end - def test_s_coonect_exc_tainted - th = Thread.start { - $SAFE = 1 - svr = "winmgmts:" - svr.taint - Thread.current.report_on_exception = false - WIN32OLE.connect(svr) - } - exc = assert_raise(SecurityError) { - th.join - } - assert_match(/insecure connection - `winmgmts:'/, exc.message) - ensure - $SAFE = 0 - end - def test_invoke_accept_symbol_hash_key fso = WIN32OLE.new('Scripting.FileSystemObject') afolder = fso.getFolder(".") diff --git a/test/win32ole/test_win32ole_event.rb b/test/win32ole/test_win32ole_event.rb index d8e162596b..742bff4f7a 100644 --- a/test/win32ole/test_win32ole_event.rb +++ b/test/win32ole/test_win32ole_event.rb @@ -401,21 +401,6 @@ if defined?(WIN32OLE_EVENT) message_loop assert(h2.ev != "") end - - def test_s_new_exc_tainted - th = Thread.new { - $SAFE=1 - str = 'ConnectionEvents' - str.taint - WIN32OLE_EVENT.new(@db, str) - } - exc = assert_raise(SecurityError) { - th.join - } - assert_match(/insecure event creation - `ConnectionEvents'/, exc.message) - ensure - $SAFE = 0 - end end end end diff --git a/thread.c b/thread.c index 15dea98b3c..52d89a01a9 100644 --- a/thread.c +++ b/thread.c @@ -3112,7 +3112,8 @@ rb_thread_stop_p(VALUE thread) static VALUE rb_thread_safe_level(VALUE thread) { - return UINT2NUM(rb_safe_level()); + rb_warn("Thread#safe_level will be removed in Ruby 3.0"); + return UINT2NUM(GET_VM()->safe_level_); } /* diff --git a/tool/lib/leakchecker.rb b/tool/lib/leakchecker.rb index 26ece9340f..57e28665d6 100644 --- a/tool/lib/leakchecker.rb +++ b/tool/lib/leakchecker.rb @@ -16,16 +16,11 @@ class LeakChecker check_tempfile_leak(test_name), check_env(test_name), check_encodings(test_name), - check_safe(test_name), check_verbose(test_name), ] GC.start if leaks.any? end - def check_safe test_name - puts "#{test_name}: $SAFE == #{$SAFE}" unless $SAFE == 0 - end - def check_verbose test_name puts "#{test_name}: $VERBOSE == #{$VERBOSE}" unless @old_verbose == $VERBOSE end diff --git a/transcode.c b/transcode.c index 86ca57b748..8a6c53595f 100644 --- a/transcode.c +++ b/transcode.c @@ -375,7 +375,7 @@ load_transcoder_entry(transcoder_entry_t *entry) rb_str_set_len(fn, total_len); FL_UNSET(fn, FL_TAINT); OBJ_FREEZE(fn); - rb_require_safe(fn, rb_safe_level()); + rb_require_string(fn); } if (entry->transcoder) diff --git a/variable.c b/variable.c index afa633151d..825f2d6271 100644 --- a/variable.c +++ b/variable.c @@ -1805,7 +1805,6 @@ struct autoload_const { VALUE ad; /* autoload_data_i */ VALUE value; ID id; - int safe_level; rb_const_flag_t flag; }; @@ -1999,7 +1998,6 @@ rb_autoload_str(VALUE mod, ID id, VALUE file) ac->mod = mod; ac->id = id; ac->value = Qundef; - ac->safe_level = rb_safe_level(); ac->flag = CONST_PUBLIC; ac->ad = ad; list_add_tail(&ele->constants, &ac->cnode); @@ -2038,20 +2036,6 @@ autoload_delete(VALUE mod, ID id) } } -static VALUE -autoload_provided(VALUE arg) -{ - const char **p = (const char **)arg; - return rb_feature_provided(*p, p); -} - -static VALUE -reset_safe(VALUE safe) -{ - rb_set_safe_level_force((int)safe); - return safe; -} - static VALUE check_autoload_required(VALUE mod, ID id, const char **loadingpath) { @@ -2059,7 +2043,6 @@ check_autoload_required(VALUE mod, ID id, const char **loadingpath) VALUE load = autoload_data(mod, id); struct autoload_data_i *ele; const char *loading; - int safe; if (!load || !(ele = get_autoload_data(load, 0))) { return 0; @@ -2081,9 +2064,7 @@ check_autoload_required(VALUE mod, ID id, const char **loadingpath) } loading = RSTRING_PTR(file); - safe = rb_safe_level(); - rb_set_safe_level_force(0); - if (!rb_ensure(autoload_provided, (VALUE)&loading, reset_safe, (VALUE)safe)) { + if (!rb_feature_provided(loading, &loading)) { return load; } if (loadingpath && loading) { @@ -2186,12 +2167,10 @@ autoload_reset(VALUE arg) /* At the last, move a value defined in autoload to constant table */ if (RTEST(state->result)) { struct autoload_const *next; - int safe_backup = rb_safe_level(); list_for_each_safe(&ele->constants, ac, next, cnode) { if (ac->value != Qundef) { - rb_ensure(autoload_const_set, (VALUE)ac, - reset_safe, (VALUE)safe_backup); + autoload_const_set((VALUE)ac); } } } diff --git a/vm_core.h b/vm_core.h index ea2e7a20cd..0946928228 100644 --- a/vm_core.h +++ b/vm_core.h @@ -619,7 +619,6 @@ typedef struct rb_vm_struct { /* signal */ struct { VALUE cmd[RUBY_NSIG]; - unsigned char safe[RUBY_NSIG]; } trap_list; /* hook */ diff --git a/vm_eval.c b/vm_eval.c index 4c73d7337e..8b557fab1c 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -1777,19 +1777,13 @@ rb_eval_string_wrap(const char *str, int *pstate) } VALUE -rb_eval_cmd(VALUE cmd, VALUE arg, int level) +rb_eval_cmd(VALUE cmd, VALUE arg, int _level) { enum ruby_tag_type state; volatile VALUE val = Qnil; /* OK */ - const int VAR_NOCLOBBERED(current_safe_level) = rb_safe_level(); rb_execution_context_t * volatile ec = GET_EC(); - if (OBJ_TAINTED(cmd)) { - level = RUBY_SAFE_LEVEL_MAX; - } - EC_PUSH_TAG(ec); - rb_set_safe_level_force(level); if ((state = EC_EXEC_TAG()) == TAG_NONE) { if (!RB_TYPE_P(cmd, T_STRING)) { val = rb_funcallv(cmd, idCall, RARRAY_LENINT(arg), @@ -1801,7 +1795,6 @@ rb_eval_cmd(VALUE cmd, VALUE arg, int level) } EC_POP_TAG(); - rb_set_safe_level_force(current_safe_level); if (state) EC_JUMP_TAG(ec, state); return val; } -- cgit v1.2.3