VALUE rb_method_call(int argc, VALUE *argv, VALUE method) { VALUE result = Qnil; /* OK */ struct METHOD *data; int state; volatile int safe = -1; Data_Get_Struct(method, struct METHOD, data); if (data->recv == Qundef) { rb_raise(rb_eTypeError, "can't call unbound method; bind first"); } PUSH_TAG(PROT_NONE); if (OBJ_TAINTED(method)) { safe = rb_safe_level(); if (rb_safe_level() < 4) { rb_set_safe_level_force(4); } } if ((state = EXEC_TAG()) == 0) { PASS_PASSED_BLOCK(); result = th_call0(GET_THREAD(), data->klass, data->recv, data->id, data->oid, argc, argv, data->body, 0); } POP_TAG(); if (safe >= 0) rb_set_safe_level_force(safe); if (state) JUMP_TAG(state); return result; }
VALUE rb_method_call(int argc, VALUE *argv, VALUE method) { VALUE result = Qnil; /* OK */ struct METHOD *data; int state; volatile int safe = -1; Data_Get_Struct(method, struct METHOD, data); if (data->recv == Qundef) { rb_raise(rb_eTypeError, "can't call unbound method; bind first"); } PUSH_TAG(); if (OBJ_TAINTED(method)) { safe = rb_safe_level(); if (rb_safe_level() < 4) { rb_set_safe_level_force(4); } } if ((state = EXEC_TAG()) == 0) { rb_thread_t *th = GET_THREAD(); VALUE rb_vm_call(rb_thread_t * th, VALUE klass, VALUE recv, VALUE id, ID oid, int argc, const VALUE *argv, const NODE *body, int nosuper); PASS_PASSED_BLOCK_TH(th); result = rb_vm_call(th, data->oclass, data->recv, data->id, data->oid, argc, argv, data->body, 0); } POP_TAG(); if (safe >= 0) rb_set_safe_level_force(safe); if (state) JUMP_TAG(state); return result; }
VALUE rb_method_call(VALUE method, SEL sel, int argc, VALUE *argv) { rb_vm_method_t *data; Data_Get_Struct(method, rb_vm_method_t, data); if (data->recv == Qundef) { rb_raise(rb_eTypeError, "can't call unbound method; bind first"); } int safe = -1; if (OBJ_TAINTED(method)) { safe = rb_safe_level(); if (rb_safe_level() < 4) { rb_set_safe_level_force(4); } } VALUE result = rb_vm_method_call(data, rb_vm_current_block(), argc, argv); if (safe >= 0) { rb_set_safe_level_force(safe); } return result; }
void rb_secure(int level) { if (level <= rb_safe_level()) { if (rb_frame_callee()) { rb_raise(rb_eSecurityError, "Insecure operation `%s' at level %d", rb_id2name(rb_frame_callee()), rb_safe_level()); } else { rb_raise(rb_eSecurityError, "Insecure operation at level %d", rb_safe_level()); } } }
VALUE rb_require_safe(VALUE fname, int safe) { volatile VALUE result = Qnil; rb_thread_t *th = GET_THREAD(); volatile VALUE errinfo = th->errinfo; int state; struct { int safe; } volatile saved; char *volatile ftptr = 0; PUSH_TAG(); saved.safe = rb_safe_level(); if ((state = EXEC_TAG()) == 0) { VALUE path; long handle; int found; rb_set_safe_level_force(safe); FilePathValue(fname); rb_set_safe_level_force(0); found = search_required(fname, &path, safe); if (found) { if (!path || !(ftptr = load_lock(RSTRING_PTR(path)))) { result = Qfalse; } else { switch (found) { case 'r': rb_load_internal(path, 0); break; case 's': handle = (long)rb_vm_call_cfunc(rb_vm_top_self(), load_ext, path, 0, path); rb_ary_push(ruby_dln_librefs, LONG2NUM(handle)); break; } rb_provide_feature(path); result = Qtrue; } } } POP_TAG(); load_unlock(ftptr, !state); rb_set_safe_level_force(saved.safe); if (state) { JUMP_TAG(state); } if (NIL_P(result)) { load_failed(fname); } th->errinfo = errinfo; return result; }
static VALUE trap(int sig, sighandler_t func, VALUE command) { sighandler_t oldfunc; VALUE oldcmd; rb_vm_t *vm = GET_VM(); /* * Be careful. ruby_signal() and trap_list[sig].cmd must be changed * atomically. In current implementation, we only need to don't call * RUBY_VM_CHECK_INTS(). */ oldfunc = ruby_signal(sig, func); oldcmd = vm->trap_list[sig].cmd; switch (oldcmd) { case 0: case Qtrue: if (oldfunc == SIG_IGN) oldcmd = rb_str_new2("IGNORE"); else if (oldfunc == sighandler) oldcmd = rb_str_new2("DEFAULT"); else oldcmd = Qnil; break; case Qnil: break; case Qundef: oldcmd = rb_str_new2("EXIT"); break; } vm->trap_list[sig].cmd = command; vm->trap_list[sig].safe = rb_safe_level(); return oldcmd; }
static int load_encoding(const char *name) { VALUE enclib = rb_sprintf("enc/%s.so", name); VALUE verbose = ruby_verbose; VALUE debug = ruby_debug; VALUE errinfo; char *s = RSTRING_PTR(enclib) + 4, *e = RSTRING_END(enclib) - 3; int loaded; int idx; while (s < e) { if (!ISALNUM(*s)) *s = '_'; else if (ISUPPER(*s)) *s = (char)TOLOWER(*s); ++s; } FL_UNSET(enclib, FL_TAINT); enclib = rb_fstring(enclib); ruby_verbose = Qfalse; ruby_debug = Qfalse; errinfo = rb_errinfo(); loaded = rb_require_internal(enclib, rb_safe_level()); ruby_verbose = verbose; ruby_debug = debug; rb_set_errinfo(errinfo); if (loaded < 0 || 1 < loaded) return -1; if ((idx = rb_enc_registered(name)) < 0) return -1; if (enc_autoload_p(enc_table.list[idx].enc)) return -1; return idx; }
/* call-seq: * unfreeze -> self * * Unfreezes the given object. Will raise a +SecurityError+ if * <tt>$SAFE</tt> > 0. Has no effect if the object is not yet frozen. */ static VALUE evilr_unfreeze(VALUE self) { if (rb_safe_level() > 0) { rb_raise(rb_eSecurityError, "can't unfreeze objects when $SAFE > 0"); } FL_UNSET(self, FL_FREEZE); return self; }
static void remove_method(VALUE klass, ID mid) { st_data_t key, data; rb_method_entry_t *me = 0; if (klass == rb_cObject) { rb_secure(4); } if (rb_safe_level() >= 4 && !OBJ_UNTRUSTED(klass)) { rb_raise(rb_eSecurityError, "Insecure: can't remove method"); } rb_check_frozen(klass); if (mid == object_id || mid == id__send__ || mid == idInitialize) { rb_warn("removing `%s' may cause serious problems", rb_id2name(mid)); } if (!st_lookup(RCLASS_M_TBL(klass), mid, &data) || !(me = (rb_method_entry_t *)data) || (!me->def || me->def->type == VM_METHOD_TYPE_UNDEF)) { rb_name_error(mid, "method `%s' not defined in %s", rb_id2name(mid), rb_class2name(klass)); } key = (st_data_t)mid; st_delete(RCLASS_M_TBL(klass), &key, &data); rb_vm_check_redefinition_opt_method(me, klass); rb_clear_cache_for_undef(klass, mid); rb_unlink_method_entry(me); CALL_METHOD_HOOK(klass, removed, mid); }
VALUE rb_f_eval(int argc, VALUE *argv, VALUE self) { VALUE src, scope, vfile, vline; const char *file = "(eval)"; int line = 1; rb_scan_args(argc, argv, "13", &src, &scope, &vfile, &vline); if (rb_safe_level() >= 4) { StringValue(src); if (!NIL_P(scope) && !OBJ_TAINTED(scope)) { rb_raise(rb_eSecurityError, "Insecure: can't modify trusted binding"); } } else { SafeStringValue(src); } if (argc >= 3) { StringValue(vfile); } if (argc >= 4) { line = NUM2INT(vline); } if (!NIL_P(vfile)) file = RSTRING_PTR(vfile); return eval_string(self, src, scope, file, line); }
static VALUE trap(struct trap_arg *arg) { sighandler_t oldfunc; sighandler_t func = arg->func; VALUE oldcmd; VALUE command = arg->cmd; int sig = arg->sig; oldfunc = ruby_signal(sig, func); oldcmd = rb_vm_trap_cmd_for_signal(sig); if (oldcmd == 0) { if (oldfunc == SIG_IGN) oldcmd = rb_str_new2("IGNORE"); else if (oldfunc == sighandler) oldcmd = rb_str_new2("DEFAULT"); else oldcmd = Qnil; } else if (oldcmd == Qundef) oldcmd = rb_str_new2("EXIT"); // Assign trap to signal rb_vm_set_trap_for_signal(command, rb_safe_level(), sig); return oldcmd; }
static inline void* getPointer(VALUE value, int type) { if (type == T_DATA && rb_obj_is_kind_of(value, rbffi_AbstractMemoryClass)) { return ((AbstractMemory *) DATA_PTR(value))->address; } else if (type == T_DATA && rb_obj_is_kind_of(value, rbffi_StructClass)) { AbstractMemory* memory = ((Struct *) DATA_PTR(value))->pointer; return memory != NULL ? memory->address : NULL; } else if (type == T_STRING) { if (rb_safe_level() >= 1 && OBJ_TAINTED(value)) { rb_raise(rb_eSecurityError, "Unsafe string parameter"); } return StringValuePtr(value); } else if (type == T_NIL) { return NULL; } else if (rb_respond_to(value, id_to_ptr)) { VALUE ptr = rb_funcall2(value, id_to_ptr, 0, NULL); if (rb_obj_is_kind_of(ptr, rbffi_AbstractMemoryClass) && TYPE(ptr) == T_DATA) { return ((AbstractMemory *) DATA_PTR(ptr))->address; } rb_raise(rb_eArgError, "to_ptr returned an invalid pointer"); } rb_raise(rb_eArgError, ":pointer argument is not a valid pointer"); return NULL; }
static VALUE trap(struct trap_arg *arg) { sighandler_t oldfunc, func = arg->func; VALUE oldcmd, command = arg->cmd; int sig = arg->sig; rb_vm_t *vm = GET_VM(); oldfunc = ruby_signal(sig, func); oldcmd = vm->trap_list[sig].cmd; switch (oldcmd) { case 0: if (oldfunc == SIG_IGN) oldcmd = rb_str_new2("IGNORE"); else if (oldfunc == sighandler) oldcmd = rb_str_new2("DEFAULT"); else oldcmd = Qnil; break; case Qundef: oldcmd = rb_str_new2("EXIT"); break; } vm->trap_list[sig].cmd = command; vm->trap_list[sig].safe = rb_safe_level(); /* enable at least specified signal. */ #if USE_TRAP_MASK #ifdef HAVE_SIGPROCMASK sigdelset(&arg->mask, sig); #else arg->mask &= ~sigmask(sig); #endif #endif return oldcmd; }
static VALUE env_aset(VALUE obj, SEL sel, VALUE nm, VALUE val) { if (rb_safe_level() >= 4) { rb_raise(rb_eSecurityError, "can't change environment variable"); } if (NIL_P(val)) { env_delete(obj, nm); return Qnil; } StringValue(nm); StringValue(val); const char *name = RSTRING_PTR(nm); const char *value = RSTRING_PTR(val); if (strlen(name) != RSTRING_LEN(nm)) { rb_raise(rb_eArgError, "bad environment variable name"); } if (strlen(value) != RSTRING_LEN(val)) { rb_raise(rb_eArgError, "bad environment variable value"); } ruby_setenv(name, value); if (strcmp(name, PATH_ENV) == 0) { if (OBJ_TAINTED(val)) { /* already tainted, no check */ path_tainted = 1; return val; } else { path_tainted_p(value); } } return val; }
/* * call-seq: * basicsocket.shutdown([how]) => 0 * * Calls shutdown(2) system call. * * s.shutdown(Socket::SHUT_RD) disallows further read. * * s.shutdown(Socket::SHUT_WR) disallows further write. * * s.shutdown(Socket::SHUT_RDWR) disallows further read and write. * * _how_ can be symbol or string: * - :RD, :SHUT_RD, "RD" and "SHUT_RD" are accepted as Socket::SHUT_RD. * - :WR, :SHUT_WR, "WR" and "SHUT_WR" are accepted as Socket::SHUT_WR. * - :RDWR, :SHUT_RDWR, "RDWR" and "SHUT_RDWR" are accepted as Socket::SHUT_RDWR. * * UNIXSocket.pair {|s1, s2| * s1.puts "ping" * s1.shutdown(:WR) * p s2.read #=> "ping\n" * s2.puts "pong" * s2.close * p s1.read #=> "pong\n" * } * */ static VALUE bsock_shutdown(int argc, VALUE *argv, VALUE sock) { UNRUBBY_SOCKET_HACK; VALUE howto; int how; rb_io_t *fptr; if (rb_safe_level() >= 4 && !OBJ_TAINTED(sock)) { rb_raise(rb_eSecurityError, "Insecure: can't shutdown socket"); } rb_scan_args(argc, argv, "01", &howto); if (howto == Qnil) how = SHUT_RDWR; else { how = rsock_shutdown_how_arg(howto); if (how != SHUT_WR && how != SHUT_RD && how != SHUT_RDWR) { rb_raise(rb_eArgError, "`how' should be either :SHUT_RD, :SHUT_WR, :SHUT_RDWR"); } } GetOpenFile(sock, fptr); if (shutdown(fptr->fd, how) == -1) rb_sys_fail(0); return INT2FIX(0); }
static VALUE fcgi_stream_gets(VALUE self) { FCGX_Stream *stream; char buff[BUFSIZ]; VALUE str = rb_str_new(0,0); OBJ_TAINT(str); if (rb_safe_level() >= 4 && !OBJ_TAINTED(self)) { rb_raise(rb_eSecurityError, "Insecure: operation on untainted IO"); } Data_Get_Struct(self, FCGX_Stream, stream); for (;;) { if (FCGX_GetLine(buff, BUFSIZ, stream) == NULL) { CHECK_STREAM_ERROR(stream); break; } rb_str_cat(str, buff, strlen(buff)); if (strchr(buff, '\n')) break; } if (RSTRING_LEN(str) > 0) return str; else return Qnil; }
VALUE rb_require(const char *fname) { VALUE fn = rb_str_new2(fname); OBJ_FREEZE(fn); return rb_require_safe(fn, rb_safe_level()); }
static VALUE memory_put_bytes(int argc, VALUE* argv, VALUE self) { AbstractMemory* ptr = MEMORY(self); VALUE offset = Qnil, str = Qnil, rbIndex = Qnil, rbLength = Qnil; long off, len, idx; int nargs = rb_scan_args(argc, argv, "22", &offset, &str, &rbIndex, &rbLength); Check_Type(str, T_STRING); off = NUM2LONG(offset); idx = nargs > 2 ? NUM2LONG(rbIndex) : 0; if (idx < 0) { rb_raise(rb_eRangeError, "index canot be less than zero"); return Qnil; } len = nargs > 3 ? NUM2LONG(rbLength) : (RSTRING_LEN(str) - idx); if ((idx + len) > RSTRING_LEN(str)) { rb_raise(rb_eRangeError, "index+length is greater than size of string"); return Qnil; } checkWrite(ptr); checkBounds(ptr, off, len); if (rb_safe_level() >= 1 && OBJ_TAINTED(str)) { rb_raise(rb_eSecurityError, "Writing unsafe string to memory"); return Qnil; } memcpy(ptr->address + off, RSTRING_PTR(str) + idx, len); return self; }
static void rb_struct_modify(VALUE s) { if (OBJ_FROZEN(s)) rb_error_frozen("Struct"); if (!OBJ_TAINTED(s) && rb_safe_level() >= 4) rb_raise(rb_eSecurityError, "Insecure: can't modify Struct"); }
static VALUE define_final(VALUE os, SEL sel, int argc, VALUE *argv) { VALUE obj, block; rb_scan_args(argc, argv, "11", &obj, &block); if (argc == 1) { block = rb_block_proc(); } else if (!rb_respond_to(block, rb_intern("call"))) { rb_raise(rb_eArgError, "wrong type argument %s (should be callable)", rb_obj_classname(block)); } if (SPECIAL_CONST_P(obj)) { rb_raise(rb_eArgError, "immediate types are not finalizable"); } rb_vm_finalizer_t *finalizer = rb_objc_get_associative_ref((void *)obj, &finalizer_key); if (finalizer == NULL) { finalizer = (rb_vm_finalizer_t *) rb_objc_newobj(sizeof(rb_vm_finalizer_t *)); finalizer->klass = rb_cFinalizer; finalizer->objid = rb_obj_id(obj, 0); GC_WB(&finalizer->finalizers, rb_ary_new()); rb_objc_set_associative_ref((void *)obj, &finalizer_key, finalizer); rb_vm_register_finalizer(finalizer); } rb_ary_push(finalizer->finalizers, block); // For RubySpec conformance. return rb_ary_new3(2, INT2FIX(rb_safe_level()), block); }
static VALUE fenix_coerce_to_path(VALUE obj) { VALUE tmp; ID to_path; rb_encoding *enc; int level = rb_safe_level(); if (insecure_obj_p(obj, level)) { rb_insecure_operation(); } CONST_ID(to_path, "to_path"); tmp = rb_check_funcall(obj, to_path, 0, 0); if (tmp == Qundef) tmp = obj; StringValue(tmp); tmp = file_path_convert(tmp); if (obj != tmp && insecure_obj_p(tmp, level)) { rb_insecure_operation(); } enc = rb_enc_get(tmp); if (!rb_enc_asciicompat(enc)) { tmp = rb_str_inspect(tmp); rb_raise(rb_eEncCompatError, "path name must be ASCII-compatible (%s): %s", rb_enc_name(enc), RSTRING_PTR(tmp)); } return rb_str_new4(tmp); }
/* * call-seq: * proc.dump(limit) => String * * Dump a Proc to a String. */ static VALUE proc_dump(VALUE self, VALUE limit) { if(rb_safe_level() >= 4) { /* no access to potentially sensitive data from the sandbox */ rb_raise(rb_eSecurityError, "Insecure: can't dump proc"); } { #ifdef RUBY_VM rb_proc_t * p; VALUE iseq, str; rb_iseq_t * iseqdat; GetProcPtr(self, p); iseq = p->block.iseq->self; iseqdat = iseq_check(iseq); iseqdat->type = ISEQ_TYPE_TOP; /* TODO: is this right? */ str = marshal_dump(iseq, limit); return str; #else struct BLOCK * b; VALUE body, var, arr; Data_Get_Struct(self, struct BLOCK, b); body = wrap_node(b->body); var = wrap_node(b->var); arr = rb_assoc_new(body, var); return marshal_dump(arr, limit); #endif } }
static void dir_check(VALUE dir) { if (!OBJ_TAINTED(dir) && rb_safe_level() >= 4) rb_raise(rb_eSecurityError, "Insecure: operation on untainted Dir"); rb_check_frozen(dir); }
void rb_error_untrusted(VALUE obj) { if (rb_safe_level() >= 4) { rb_raise(rb_eSecurityError, "Insecure: can't modify %s", rb_obj_classname(obj)); } }
static void secure_visibility(VALUE self) { if (rb_safe_level() >= 4 && !OBJ_TAINTED(self)) { rb_raise(rb_eSecurityError, "Insecure: can't change method visibility"); } }
VALUE rb_format_exception_message(VALUE exc) { const int old_level = rb_safe_level(); rb_set_safe_level_force(0); return rb_ensure(format_message, exc, restore_level, (VALUE)old_level); }
void rb_check_safe_obj(VALUE x) { if (rb_safe_level() > 0 && OBJ_TAINTED(x)) { rb_insecure_operation(); } rb_secure(4); }
void rb_undef(VALUE klass, ID id) { // TODO #if 0 VALUE origin; NODE *body; #if 0 // TODO if (rb_vm_cbase() == rb_cObject && klass == rb_cObject) { rb_secure(4); } #endif if (rb_safe_level() >= 4 && !OBJ_TAINTED(klass)) { rb_raise(rb_eSecurityError, "Insecure: can't undef `%s'", rb_id2name(id)); } rb_frozen_class_p(klass); if (id == object_id || id == __send__ || id == idInitialize) { rb_warn("undefining `%s' may cause serious problem", rb_id2name(id)); } /* TODO: warn if a very important method of NSObject is undefined * by default, pure objc methods are not exposed by introspections API */ body = search_method(klass, id, &origin); if (!body || !body->nd_body) { const char *s0 = " class"; VALUE c = klass; if (RCLASS_SINGLETON(c)) { VALUE obj = rb_iv_get(klass, "__attached__"); switch (TYPE(obj)) { case T_MODULE: case T_CLASS: c = obj; s0 = ""; } } else if (TYPE(c) == T_MODULE) { s0 = " module"; } rb_name_error(id, "undefined method `%s' for%s `%s'", rb_id2name(id), s0, rb_class2name(c)); } rb_add_method(klass, id, 0, NOEX_PUBLIC); if (RCLASS_SINGLETON(klass)) { rb_funcall(rb_iv_get(klass, "__attached__"), singleton_undefined, 1, ID2SYM(id)); } else { rb_funcall(klass, undefined, 1, ID2SYM(id)); } #endif }
/* * call-seq: * require_relative(string) -> true or false * * Ruby tries to load the library named _string_ relative to the requiring * file's path. If the file's path cannot be determined a LoadError is raised. * If a file is loaded +true+ is returned and false otherwise. */ VALUE rb_f_require_relative(VALUE obj, VALUE fname) { VALUE base = rb_current_realfilepath(); if (NIL_P(base)) { rb_loaderror("cannot infer basepath"); } base = rb_file_dirname(base); return rb_require_safe(rb_file_absolute_path(fname, base), rb_safe_level()); }
static void init_sigchld(int sig) { sighandler_t oldfunc; oldfunc = ruby_signal(sig, SIG_DFL); if (oldfunc != SIG_DFL && oldfunc != SIG_IGN) { ruby_signal(sig, oldfunc); } else { rb_vm_set_trap_for_signal((VALUE)0, rb_safe_level(), sig); } }