string: preserve taint flag with String#-@ (uminus)

* string.c (tainted_fstr_update): move up
  (rb_fstring): support registering tainted strings
  (register_fstring_tainted): extract from rb_fstring_existing0
  (rb_tainted_fstring_existing): use register_fstring_tainted instead

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@59359 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
normal 2017-07-18 09:52:55 +00:00
parent 1f67a3900f
commit 86e266bb60
2 changed files with 78 additions and 61 deletions

134
string.c
View File

@ -301,54 +301,6 @@ fstr_update_callback(st_data_t *key, st_data_t *value, st_data_t arg, int existi
} }
} }
RUBY_FUNC_EXPORTED
VALUE
rb_fstring(VALUE str)
{
VALUE fstr;
int bare;
Check_Type(str, T_STRING);
if (FL_TEST(str, RSTRING_FSTR))
return str;
bare = BARE_STRING_P(str);
if (STR_EMBED_P(str) && !bare) {
OBJ_FREEZE_RAW(str);
return str;
}
fstr = register_fstring(str);
if (!bare) {
str_replace_shared_without_enc(str, fstr);
OBJ_FREEZE_RAW(str);
return str;
}
return fstr;
}
static VALUE
register_fstring(VALUE str)
{
VALUE ret;
st_table *frozen_strings = rb_vm_fstring_table();
do {
ret = str;
st_update(frozen_strings, (st_data_t)str,
fstr_update_callback, (st_data_t)&ret);
} while (ret == Qundef);
assert(OBJ_FROZEN(ret));
assert(!FL_TEST_RAW(ret, STR_FAKESTR));
assert(!FL_TEST_RAW(ret, FL_EXIVAR));
assert(!FL_TEST_RAW(ret, FL_TAINT));
assert(RBASIC_CLASS(ret) == rb_cString);
return ret;
}
static int static int
tainted_fstr_update(st_data_t *key, st_data_t *val, st_data_t arg, int existing) tainted_fstr_update(st_data_t *key, st_data_t *val, st_data_t arg, int existing)
{ {
@ -377,6 +329,78 @@ tainted_fstr_update(st_data_t *key, st_data_t *val, st_data_t arg, int existing)
} }
} }
static VALUE
register_fstring_tainted(VALUE str, st_table *tfstrings)
{
st_data_t fstr;
do {
fstr = (st_data_t)str;
st_update(tfstrings, fstr, tainted_fstr_update, (st_data_t)&fstr);
} while ((VALUE)fstr == Qundef);
str = (VALUE)fstr;
assert(OBJ_FROZEN_RAW(str));
assert(!FL_TEST_RAW(str, STR_FAKESTR));
assert(!FL_TEST_RAW(str, FL_EXIVAR));
assert(FL_TEST_RAW(str, RSTRING_FSTR));
assert(FL_TEST_RAW(str, FL_TAINT));
assert(RBASIC_CLASS(str) == rb_cString);
return str;
}
RUBY_FUNC_EXPORTED
VALUE
rb_fstring(VALUE str)
{
VALUE fstr;
int bare_ish;
Check_Type(str, T_STRING);
if (FL_TEST(str, RSTRING_FSTR))
return str;
bare_ish = !FL_TEST_RAW(str, FL_EXIVAR) && RBASIC_CLASS(str) == rb_cString;
if (STR_EMBED_P(str) && !bare_ish) {
OBJ_FREEZE_RAW(str);
return str;
}
if (!FL_TEST_RAW(str, FL_TAINT)) {
fstr = register_fstring(str);
}
else {
fstr = register_fstring_tainted(str, rb_vm_tfstring_table());
}
if (!bare_ish) {
str_replace_shared_without_enc(str, fstr);
OBJ_FREEZE_RAW(str);
return str;
}
return fstr;
}
static VALUE
register_fstring(VALUE str)
{
VALUE ret;
st_table *frozen_strings = rb_vm_fstring_table();
do {
ret = str;
st_update(frozen_strings, (st_data_t)str,
fstr_update_callback, (st_data_t)&ret);
} while (ret == Qundef);
assert(OBJ_FROZEN(ret));
assert(!FL_TEST_RAW(ret, STR_FAKESTR));
assert(!FL_TEST_RAW(ret, FL_EXIVAR));
assert(!FL_TEST_RAW(ret, FL_TAINT));
assert(RBASIC_CLASS(ret) == rb_cString);
return ret;
}
static VALUE static VALUE
rb_fstring_existing0(VALUE str) rb_fstring_existing0(VALUE str)
{ {
@ -414,20 +438,8 @@ rb_tainted_fstring_existing(VALUE str)
if (!RB_FL_TEST_RAW(ret, RSTRING_FSTR)) { if (!RB_FL_TEST_RAW(ret, RSTRING_FSTR)) {
return Qnil; return Qnil;
} }
do {
fstr = (st_data_t)ret;
st_update(tfstrings, fstr, tainted_fstr_update, (st_data_t)&fstr);
} while ((VALUE)fstr == Qundef);
ret = (VALUE)fstr; return register_fstring_tainted(str, tfstrings);
assert(OBJ_FROZEN_RAW(ret));
assert(!FL_TEST_RAW(ret, STR_FAKESTR));
assert(!FL_TEST_RAW(ret, FL_EXIVAR));
assert(FL_TEST_RAW(ret, RSTRING_FSTR));
assert(FL_TEST_RAW(ret, FL_TAINT));
assert(RBASIC_CLASS(ret) == rb_cString);
return ret;
} }
VALUE VALUE

View File

@ -2749,10 +2749,15 @@ CODE
assert_not_equal(str.object_id, (+str).object_id) assert_not_equal(str.object_id, (+str).object_id)
assert_equal(str.object_id, (-str).object_id) assert_equal(str.object_id, (-str).object_id)
return unless @cls == String
bar = %w(b a r).join('') bar = %w(b a r).join('')
assert_not_predicate bar, :tainted? assert_not_predicate bar, :tainted?
assert_not_predicate str, :tainted? assert_not_predicate str, :tainted?
assert_same(str, -bar, "uminus deduplicates [Feature #13077]") assert_same(str, -bar, "uminus deduplicates [Feature #13077]")
bar = %w(b a r).taint.join('')
tstr = str.dup.taint
assert_same -tstr, -bar
end end
def test_ord def test_ord