THRIFT-1182. rb: Native deserializer segfaults on incorrect list element type
This patch causes both the pure ruby and native extension code paths to check if the data in lists, sets, and maps is of the expected type before deserlizing it. When it's not the right type, it now skips the bad data correctly.
Patch: Ilya Maykov
git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1129892 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/rb/ext/struct.c b/lib/rb/ext/struct.c
index db78efa..90c4b05 100644
--- a/lib/rb/ext/struct.c
+++ b/lib/rb/ext/struct.c
@@ -414,6 +414,8 @@
static VALUE rb_thrift_union_read(VALUE self, VALUE protocol);
static VALUE rb_thrift_struct_read(VALUE self, VALUE protocol);
+static void skip_map_contents(VALUE protocol, VALUE key_type_value, VALUE value_type_value, int size);
+static void skip_list_or_set_contents(VALUE protocol, VALUE element_type_value, int size);
static void set_field_value(VALUE obj, VALUE field_name, VALUE value) {
char name_buf[RSTRING_LEN(field_name) + 1];
@@ -424,6 +426,23 @@
rb_ivar_set(obj, rb_intern(name_buf), value);
}
+// Helper method to skip the contents of a map (assumes the map header has been read).
+static void skip_map_contents(VALUE protocol, VALUE key_type_value, VALUE value_type_value, int size) {
+ int i;
+ for (i = 0; i < size; i++) {
+ rb_funcall(protocol, skip_method_id, 1, key_type_value);
+ rb_funcall(protocol, skip_method_id, 1, value_type_value);
+ }
+}
+
+// Helper method to skip the contents of a list or set (assumes the list/set header has been read).
+static void skip_list_or_set_contents(VALUE protocol, VALUE element_type_value, int size) {
+ int i;
+ for (i = 0; i < size; i++) {
+ rb_funcall(protocol, skip_method_id, 1, element_type_value);
+ }
+}
+
static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
VALUE result = Qnil;
@@ -458,18 +477,30 @@
int value_ttype = FIX2INT(rb_ary_entry(map_header, 1));
int num_entries = FIX2INT(rb_ary_entry(map_header, 2));
+ // Check the declared key and value types against the expected ones and skip the map contents
+ // if the types don't match.
VALUE key_info = rb_hash_aref(field_info, key_sym);
VALUE value_info = rb_hash_aref(field_info, value_sym);
- result = rb_hash_new();
+ if (!NIL_P(key_info) && !NIL_P(value_info)) {
+ int specified_key_type = FIX2INT(rb_hash_aref(key_info, type_sym));
+ int specified_value_type = FIX2INT(rb_hash_aref(value_info, type_sym));
+ if (specified_key_type == key_ttype && specified_value_type == value_ttype) {
+ result = rb_hash_new();
- for (i = 0; i < num_entries; ++i) {
- VALUE key, val;
+ for (i = 0; i < num_entries; ++i) {
+ VALUE key, val;
- key = read_anything(protocol, key_ttype, key_info);
- val = read_anything(protocol, value_ttype, value_info);
+ key = read_anything(protocol, key_ttype, key_info);
+ val = read_anything(protocol, value_ttype, value_info);
- rb_hash_aset(result, key, val);
+ rb_hash_aset(result, key, val);
+ }
+ } else {
+ skip_map_contents(protocol, INT2FIX(key_ttype), INT2FIX(value_ttype), num_entries);
+ }
+ } else {
+ skip_map_contents(protocol, INT2FIX(key_ttype), INT2FIX(value_ttype), num_entries);
}
default_read_map_end(protocol);
@@ -479,10 +510,23 @@
VALUE list_header = default_read_list_begin(protocol);
int element_ttype = FIX2INT(rb_ary_entry(list_header, 0));
int num_elements = FIX2INT(rb_ary_entry(list_header, 1));
- result = rb_ary_new2(num_elements);
- for (i = 0; i < num_elements; ++i) {
- rb_ary_push(result, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
+ // Check the declared element type against the expected one and skip the list contents
+ // if the types don't match.
+ VALUE element_info = rb_hash_aref(field_info, element_sym);
+ if (!NIL_P(element_info)) {
+ int specified_element_type = FIX2INT(rb_hash_aref(element_info, type_sym));
+ if (specified_element_type == element_ttype) {
+ result = rb_ary_new2(num_elements);
+
+ for (i = 0; i < num_elements; ++i) {
+ rb_ary_push(result, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
+ }
+ } else {
+ skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
+ }
+ } else {
+ skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
}
default_read_list_end(protocol);
@@ -493,15 +537,28 @@
VALUE set_header = default_read_set_begin(protocol);
int element_ttype = FIX2INT(rb_ary_entry(set_header, 0));
int num_elements = FIX2INT(rb_ary_entry(set_header, 1));
- items = rb_ary_new2(num_elements);
- for (i = 0; i < num_elements; ++i) {
- rb_ary_push(items, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
+ // Check the declared element type against the expected one and skip the set contents
+ // if the types don't match.
+ VALUE element_info = rb_hash_aref(field_info, element_sym);
+ if (!NIL_P(element_info)) {
+ int specified_element_type = FIX2INT(rb_hash_aref(element_info, type_sym));
+ if (specified_element_type == element_ttype) {
+ items = rb_ary_new2(num_elements);
+
+ for (i = 0; i < num_elements; ++i) {
+ rb_ary_push(items, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
+ }
+
+ result = rb_class_new_instance(1, &items, rb_cSet);
+ } else {
+ skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
+ }
+ } else {
+ skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
}
default_read_set_end(protocol);
-
- result = rb_class_new_instance(1, &items, rb_cSet);
} else {
rb_raise(rb_eNotImpError, "read_anything not implemented for type %d!", ttype);
}