summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog22
-rw-r--r--include/ruby/intern.h1
-rw-r--r--inits.c2
-rw-r--r--random.c76
-rw-r--r--string.c12
-rw-r--r--test/ruby/test_string.rb11
-rw-r--r--thread.c1
7 files changed, 107 insertions, 18 deletions
diff --git a/ChangeLog b/ChangeLog
index 6a2d69eb2b..643f035cc3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,25 @@
+Fri Jun 20 11:07:56 2008 Tanaka Akira <akr@fsij.org>
+
+ * string.c (rb_memhash): randomize hash to avoid algorithmic
+ complexity attacks.
+ (rb_str_hash): use rb_memhash.
+
+ * include/ruby/intern.h (rb_reset_random_seed): declared.
+
+ * thread.c (rb_thread_atfork): call rb_reset_random_seed.
+
+ * inits.c (rb_call_inits): call Init_RandomSeed at first.
+
+ * random.c (seed_initialized): defined.
+ (fill_random_seed): extracted from random_seed.
+ (make_seed_value): extracted from random_seed.
+ (rb_f_rand): initialize random seed at first.
+ (initial_seed): defined.
+ (Init_RandomSeed): defined.
+ (Init_RandomSeed2): defined.
+ (rb_reset_random_seed): defined.
+ (Init_Random): call Init_RandomSeed2.
+
Wed Jun 18 21:52:38 2008 URABE Shyouhei <shyouhei@ruby-lang.org>
* array.c (ary_new, rb_ary_initialize, rb_ary_store,
diff --git a/include/ruby/intern.h b/include/ruby/intern.h
index a6f85482e9..36ae8442bb 100644
--- a/include/ruby/intern.h
+++ b/include/ruby/intern.h
@@ -475,6 +475,7 @@ VALUE rb_range_beg_len(VALUE, long*, long*, long, int);
/* random.c */
unsigned long rb_genrand_int32(void);
double rb_genrand_real(void);
+void rb_reset_random_seed(void);
/* re.c */
#define rb_memcmp memcmp
int rb_memcicmp(const void*,const void*,long);
diff --git a/inits.c b/inits.c
index d031fcf08f..5e55d7f69d 100644
--- a/inits.c
+++ b/inits.c
@@ -38,6 +38,7 @@ void Init_Precision(void);
void Init_sym(void);
void Init_id(void);
void Init_process(void);
+void Init_RandomSeed(void);
void Init_Random(void);
void Init_Range(void);
void Init_Rational(void);
@@ -58,6 +59,7 @@ void Init_Encoding(void);
void
rb_call_inits()
{
+ Init_RandomSeed();
Init_sym();
Init_id();
Init_var_tables();
diff --git a/random.c b/random.c
index d0849ebc93..83fb16bd94 100644
--- a/random.c
+++ b/random.c
@@ -199,6 +199,7 @@ rb_genrand_real(void)
return genrand_real();
}
+static int seed_initialized = 0;
static VALUE saved_seed = INT2FIX(0);
static VALUE
@@ -259,27 +260,21 @@ rand_init(VALUE vseed)
return old;
}
-static VALUE
-random_seed(void)
+#define DEFAULT_SEED_LEN (4 * sizeof(long))
+
+static void
+fill_random_seed(char *ptr)
{
static int n = 0;
+ unsigned long *seed;
struct timeval tv;
int fd;
struct stat statbuf;
+ char *buf = (char*)ptr;
- int seed_len;
- BDIGIT *digits;
- unsigned long *seed;
- NEWOBJ(big, struct RBignum);
- OBJSETUP(big, rb_cBignum, T_BIGNUM);
+ seed = (unsigned long *)buf;
- seed_len = 4 * sizeof(long);
- RBIGNUM_SET_SIGN(big, 1);
- rb_big_resize((VALUE)big, seed_len / SIZEOF_BDIGITS + 1);
- digits = RBIGNUM_DIGITS(big);
- seed = (unsigned long *)RBIGNUM_DIGITS(big);
-
- memset(digits, 0, RBIGNUM_LEN(big) * SIZEOF_BDIGITS);
+ memset(buf, 0, DEFAULT_SEED_LEN);
#ifdef S_ISCHR
if ((fd = open("/dev/urandom", O_RDONLY
@@ -294,7 +289,7 @@ random_seed(void)
#endif
)) >= 0) {
if (fstat(fd, &statbuf) == 0 && S_ISCHR(statbuf.st_mode)) {
- read(fd, seed, seed_len);
+ read(fd, seed, DEFAULT_SEED_LEN);
}
close(fd);
}
@@ -305,6 +300,20 @@ random_seed(void)
seed[1] ^= tv.tv_sec;
seed[2] ^= getpid() ^ (n++ << 16);
seed[3] ^= (unsigned long)&seed;
+}
+
+static VALUE
+make_seed_value(char *ptr)
+{
+ BDIGIT *digits;
+ NEWOBJ(big, struct RBignum);
+ OBJSETUP(big, rb_cBignum, T_BIGNUM);
+
+ RBIGNUM_SET_SIGN(big, 1);
+ rb_big_resize((VALUE)big, DEFAULT_SEED_LEN / SIZEOF_BDIGITS + 1);
+ digits = RBIGNUM_DIGITS(big);
+
+ MEMCPY((char *)RBIGNUM_DIGITS(big), ptr, char, DEFAULT_SEED_LEN);
/* set leading-zero-guard if need. */
digits[RBIGNUM_LEN(big)-1] = digits[RBIGNUM_LEN(big)-2] <= 1 ? 1 : 0;
@@ -312,6 +321,14 @@ random_seed(void)
return rb_big_norm((VALUE)big);
}
+static VALUE
+random_seed(void)
+{
+ char buf[DEFAULT_SEED_LEN];
+ fill_random_seed(buf);
+ return make_seed_value(buf);
+}
+
/*
* call-seq:
* srand(number=0) => old_seed
@@ -453,6 +470,9 @@ rb_f_rand(int argc, VALUE *argv, VALUE obj)
long val, max;
rb_scan_args(argc, argv, "01", &vmax);
+ if (!seed_initialized) {
+ rand_init(random_seed());
+ }
switch (TYPE(vmax)) {
case T_FLOAT:
if (RFLOAT_VALUE(vmax) <= LONG_MAX && RFLOAT_VALUE(vmax) >= LONG_MIN) {
@@ -499,10 +519,34 @@ rb_f_rand(int argc, VALUE *argv, VALUE obj)
return LONG2NUM(val);
}
+static char initial_seed[DEFAULT_SEED_LEN];
+
+void
+Init_RandomSeed(void)
+{
+ fill_random_seed(initial_seed);
+ init_by_array((unsigned long*)initial_seed, DEFAULT_SEED_LEN/sizeof(unsigned long));
+ seed_initialized = 1;
+}
+
+static VALUE
+Init_RandomSeed2(void)
+{
+ saved_seed = make_seed_value(initial_seed);
+ memset(initial_seed, 0, DEFAULT_SEED_LEN);
+}
+
+void
+rb_reset_random_seed(void)
+{
+ seed_initialized = 0;
+ saved_seed = INT2FIX(0);
+}
+
void
Init_Random(void)
{
- rand_init(random_seed());
+ Init_RandomSeed2();
rb_define_global_function("srand", rb_f_srand, -1);
rb_define_global_function("rand", rb_f_rand, -1);
rb_global_variable(&saved_seed);
diff --git a/string.c b/string.c
index c19544deac..5c46d61605 100644
--- a/string.c
+++ b/string.c
@@ -1820,13 +1820,21 @@ hash(const unsigned char * data, int len, unsigned int h)
int
rb_memhash(const void *ptr, long len)
{
- return hash(ptr, len, 0);
+ static int hashseed_init = 0;
+ static unsigned int hashseed;
+
+ if (!hashseed_init) {
+ hashseed = rb_genrand_int32();
+ hashseed_init = 1;
+ }
+
+ return hash(ptr, len, hashseed);
}
int
rb_str_hash(VALUE str)
{
- return hash((const void *)RSTRING_PTR(str), RSTRING_LEN(str), 0);
+ return rb_memhash((const void *)RSTRING_PTR(str), RSTRING_LEN(str));
}
int
diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb
index 55cef9001e..869e31b641 100644
--- a/test/ruby/test_string.rb
+++ b/test/ruby/test_string.rb
@@ -683,6 +683,17 @@ class TestString < Test::Unit::TestCase
assert(S("hello").hash != S("helLO").hash)
end
+ def test_hash_random
+ str = 'abc'
+ a = [str.hash.to_s]
+ 3.times {
+ EnvUtil.rubyexec("-e", "print #{str.dump}.hash") {|i,o,e|
+ a << o.read
+ }
+ }
+ assert_not_equal([str.hash.to_s], a.uniq)
+ end
+
def test_hex
assert_equal(255, S("0xff").hex)
assert_equal(-255, S("-0xff").hex)
diff --git a/thread.c b/thread.c
index 92f5104dba..13630b6b02 100644
--- a/thread.c
+++ b/thread.c
@@ -2124,6 +2124,7 @@ rb_thread_atfork(void)
st_clear(vm->living_threads);
st_insert(vm->living_threads, thval, (st_data_t) th->thread_id);
vm->sleeper = 0;
+ rb_reset_random_seed();
}
static int