CAN-2005-2337.diff   [plain text]


--- eval.c.orig	2004-12-18 11:07:29.000000000 +0900
+++ eval.c	2005-09-21 16:53:31.127896405 +0900
@@ -254,2 +254,7 @@
 
+#define NOEX_TAINTED 8
+#define NOEX_SAFE(n) ((n) >> 4)
+#define NOEX_WITH(n, v) ((n) | (v) << 4)
+#define NOEX_WITH_SAFE(n) NOEX_WITH(n, ruby_safe_level)
+
 void
@@ -346,3 +351,3 @@
     rb_clear_cache_by_id(mid);
-    body = NEW_METHOD(node, noex);
+    body = NEW_METHOD(node, NOEX_WITH_SAFE(noex));
     st_insert(RCLASS(klass)->m_tbl, mid, (st_data_t)body);
@@ -5458,3 +5463,3 @@
 static VALUE
-rb_call0(klass, recv, id, oid, argc, argv, body, nosuper)
+rb_call0(klass, recv, id, oid, argc, argv, body, flags)
     VALUE klass, recv;
@@ -5465,3 +5470,3 @@
     NODE *body;			/* OK */
-    int nosuper;
+    int flags;
 {
@@ -5472,2 +5477,3 @@
     TMP_PROTECT;
+    volatile int safe = -1;
 
@@ -5493,3 +5499,3 @@
     ruby_frame->orig_func = oid;
-    ruby_frame->last_class = nosuper?0:klass;
+    ruby_frame->last_class = (flags & NOEX_UNDEF)?0:klass;
     ruby_frame->self = recv;
@@ -5555,3 +5561,2 @@
 	    PUSH_SCOPE();
-
 	    if (body->nd_rval) {
@@ -5574,5 +5579,12 @@
 
+	    if (NOEX_SAFE(flags) > ruby_safe_level) {
+		if (!(flags&NOEX_TAINTED) && ruby_safe_level == 0 && NOEX_SAFE(flags) > 2) {
+		    rb_raise(rb_eSecurityError, "calling insecure method: %s",
+			     rb_id2name(id));
+		}
+		safe = ruby_safe_level;
+		ruby_safe_level = NOEX_SAFE(flags);
+	    }
 	    PUSH_VARS();
 	    PUSH_TAG(PROT_FUNC);
-
 	    if ((state = EXEC_TAG()) == 0) {
@@ -5655,2 +5667,3 @@
 	    }
+	    if (safe >= 0) ruby_safe_level = safe;
 	    POP_TAG();
@@ -5742,3 +5755,3 @@
 
-    return rb_call0(klass, recv, mid, id, argc, argv, body, noex & NOEX_NOSUPER);
+    return rb_call0(klass, recv, mid, id, argc, argv, body, noex);
 }
@@ -8532,2 +8545,3 @@
     ID id, oid;
+    int safe_level;
     NODE *body;
@@ -8579,2 +8593,3 @@
     data->oid = oid;
+    data->safe_level = NOEX_WITH_SAFE(0);
     OBJ_INFECT(method, klass);
@@ -8663,2 +8678,3 @@
     data->oid = orig->oid;
+    data->safe_level = NOEX_WITH_SAFE(0);
     OBJ_INFECT(method, obj);
@@ -8784,4 +8800,3 @@
     struct METHOD *data;
-    int state;
-    volatile int safe = -1;
+    int safe;
 
@@ -8791,15 +8806,11 @@
     }
-    PUSH_ITER(rb_block_given_p()?ITER_PRE:ITER_NOT);
-    PUSH_TAG(PROT_NONE);
     if (OBJ_TAINTED(method)) {
-	safe = ruby_safe_level;
-	if (ruby_safe_level < 4) ruby_safe_level = 4;
+        safe = NOEX_WITH(data->safe_level, 4)|NOEX_TAINTED;
     }
-    if ((state = EXEC_TAG()) == 0) {
-	result = rb_call0(data->klass,data->recv,data->id,data->oid,argc,argv,data->body,0);
+    else {
+	safe = data->safe_level;
     }
-    POP_TAG();
+    PUSH_ITER(rb_block_given_p()?ITER_PRE:ITER_NOT);
+    result = rb_call0(data->klass,data->recv,data->id,data->oid,argc,argv,data->body,safe);
     POP_ITER();
-    if (safe >= 0) ruby_safe_level = safe;
-    if (state) JUMP_TAG(state);
     return result;