thrift 0.23.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +203 -40
  3. data/ext/binary_protocol_accelerated.c +130 -40
  4. data/ext/bytes.c +14 -0
  5. data/ext/compact_protocol.c +184 -91
  6. data/ext/constants.h +3 -0
  7. data/ext/extconf.rb +12 -4
  8. data/ext/macros.h +11 -0
  9. data/ext/memory_buffer.c +67 -12
  10. data/ext/struct.c +192 -71
  11. data/ext/thrift_native.c +7 -0
  12. data/lib/thrift/bytes.rb +6 -3
  13. data/lib/thrift/client.rb +31 -33
  14. data/lib/thrift/exceptions.rb +11 -8
  15. data/lib/thrift/multiplexed_processor.rb +17 -8
  16. data/lib/thrift/processor.rb +25 -3
  17. data/lib/thrift/protocol/base_protocol.rb +66 -31
  18. data/lib/thrift/protocol/binary_protocol.rb +80 -37
  19. data/lib/thrift/protocol/binary_protocol_accelerated.rb +7 -2
  20. data/lib/thrift/protocol/compact_protocol.rb +204 -88
  21. data/lib/thrift/protocol/header_protocol.rb +15 -3
  22. data/lib/thrift/protocol/json_protocol.rb +158 -109
  23. data/lib/thrift/protocol/multiplexed_protocol.rb +3 -3
  24. data/lib/thrift/protocol/protocol_decorator.rb +6 -2
  25. data/lib/thrift/serializer/deserializer.rb +1 -0
  26. data/lib/thrift/serializer/serializer.rb +7 -0
  27. data/lib/thrift/server/base_server.rb +11 -2
  28. data/lib/thrift/server/nonblocking_server.rb +69 -20
  29. data/lib/thrift/server/rack_application.rb +69 -0
  30. data/lib/thrift/server/simple_server.rb +27 -15
  31. data/lib/thrift/server/thin_http_server.rb +21 -44
  32. data/lib/thrift/server/thread_pool_server.rb +21 -17
  33. data/lib/thrift/server/threaded_server.rb +22 -18
  34. data/lib/thrift/struct.rb +34 -18
  35. data/lib/thrift/struct_union.rb +37 -36
  36. data/lib/thrift/thrift_native.rb +2 -1
  37. data/lib/thrift/transport/base_server_transport.rb +3 -0
  38. data/lib/thrift/transport/base_transport.rb +26 -4
  39. data/lib/thrift/transport/buffered_transport.rb +5 -4
  40. data/lib/thrift/transport/framed_transport.rb +33 -7
  41. data/lib/thrift/transport/header_transport.rb +159 -33
  42. data/lib/thrift/transport/http_client_transport.rb +28 -14
  43. data/lib/thrift/transport/io_stream_transport.rb +3 -1
  44. data/lib/thrift/transport/memory_buffer_transport.rb +14 -9
  45. data/lib/thrift/transport/server_socket.rb +10 -8
  46. data/lib/thrift/transport/socket.rb +85 -48
  47. data/lib/thrift/transport/ssl_server_socket.rb +67 -6
  48. data/lib/thrift/transport/ssl_socket.rb +73 -14
  49. data/lib/thrift/transport/unix_server_socket.rb +6 -2
  50. data/lib/thrift/transport/unix_socket.rb +5 -6
  51. data/lib/thrift/types.rb +23 -22
  52. data/lib/thrift/union.rb +21 -11
  53. data/lib/thrift/uuid.rb +9 -8
  54. data/lib/thrift.rb +40 -39
  55. metadata +7 -266
  56. data/benchmark/Benchmark.thrift +0 -24
  57. data/benchmark/benchmark.rb +0 -285
  58. data/benchmark/client.rb +0 -117
  59. data/benchmark/server.rb +0 -120
  60. data/benchmark/thin_server.rb +0 -45
  61. data/lib/thrift/server/mongrel_http_server.rb +0 -60
  62. data/spec/BaseService.thrift +0 -27
  63. data/spec/ExtendedService.thrift +0 -25
  64. data/spec/Referenced.thrift +0 -44
  65. data/spec/ThriftNamespacedSpec.thrift +0 -53
  66. data/spec/ThriftSpec.thrift +0 -187
  67. data/spec/base_protocol_spec.rb +0 -224
  68. data/spec/base_transport_spec.rb +0 -387
  69. data/spec/binary_protocol_accelerated_spec.rb +0 -46
  70. data/spec/binary_protocol_spec.rb +0 -72
  71. data/spec/binary_protocol_spec_shared.rb +0 -445
  72. data/spec/bytes_spec.rb +0 -99
  73. data/spec/client_spec.rb +0 -166
  74. data/spec/compact_protocol_spec.rb +0 -196
  75. data/spec/constants_demo_spec.rb +0 -101
  76. data/spec/exception_spec.rb +0 -140
  77. data/spec/flat_spec.rb +0 -62
  78. data/spec/header_protocol_spec.rb +0 -475
  79. data/spec/header_transport_spec.rb +0 -386
  80. data/spec/http_client_spec.rb +0 -159
  81. data/spec/json_protocol_spec.rb +0 -552
  82. data/spec/namespaced_spec.rb +0 -66
  83. data/spec/nonblocking_server_spec.rb +0 -361
  84. data/spec/processor_spec.rb +0 -79
  85. data/spec/serializer_spec.rb +0 -66
  86. data/spec/server_socket_spec.rb +0 -84
  87. data/spec/server_spec.rb +0 -186
  88. data/spec/socket_spec.rb +0 -67
  89. data/spec/socket_spec_shared.rb +0 -167
  90. data/spec/spec_helper.rb +0 -69
  91. data/spec/ssl_server_socket_spec.rb +0 -45
  92. data/spec/ssl_socket_spec.rb +0 -87
  93. data/spec/struct_nested_containers_spec.rb +0 -190
  94. data/spec/struct_spec.rb +0 -397
  95. data/spec/support/header_protocol_helper.rb +0 -54
  96. data/spec/thin_http_server_spec.rb +0 -126
  97. data/spec/types_spec.rb +0 -117
  98. data/spec/union_spec.rb +0 -272
  99. data/spec/unix_socket_spec.rb +0 -115
  100. data/spec/uuid_validation_spec.rb +0 -238
  101. data/test/fuzz/Makefile.am +0 -173
  102. data/test/fuzz/README.md +0 -149
  103. data/test/fuzz/fuzz_common.rb +0 -95
  104. data/test/fuzz/fuzz_parse_binary_protocol.rb +0 -22
  105. data/test/fuzz/fuzz_parse_binary_protocol_accelerated.rb +0 -22
  106. data/test/fuzz/fuzz_parse_binary_protocol_accelerated_harness.rb +0 -22
  107. data/test/fuzz/fuzz_parse_binary_protocol_harness.rb +0 -22
  108. data/test/fuzz/fuzz_parse_compact_protocol.rb +0 -22
  109. data/test/fuzz/fuzz_parse_compact_protocol_harness.rb +0 -22
  110. data/test/fuzz/fuzz_parse_json_protocol.rb +0 -22
  111. data/test/fuzz/fuzz_parse_json_protocol_harness.rb +0 -22
  112. data/test/fuzz/fuzz_roundtrip_binary_protocol.rb +0 -22
  113. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated.rb +0 -22
  114. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated_harness.rb +0 -22
  115. data/test/fuzz/fuzz_roundtrip_binary_protocol_harness.rb +0 -22
  116. data/test/fuzz/fuzz_roundtrip_compact_protocol.rb +0 -22
  117. data/test/fuzz/fuzz_roundtrip_compact_protocol_harness.rb +0 -22
  118. data/test/fuzz/fuzz_roundtrip_json_protocol.rb +0 -22
  119. data/test/fuzz/fuzz_roundtrip_json_protocol_harness.rb +0 -22
  120. data/test/fuzz/fuzz_tracer.rb +0 -28
@@ -62,9 +62,43 @@ static int CTYPE_MAP = 0x0B;
62
62
  static int CTYPE_STRUCT = 0x0C;
63
63
  static int CTYPE_UUID = 0x0D;
64
64
 
65
- VALUE rb_thrift_compact_proto_write_i16(VALUE self, VALUE i16);
65
+ static uint32_t int_to_zig_zag(int32_t n);
66
+ static void write_varint32(VALUE transport, uint32_t n);
67
+
68
+ static int64_t checked_integer_range(VALUE value, int64_t min, int64_t max) {
69
+ CHECK_NIL(value);
70
+ if (RB_UNLIKELY(!RB_INTEGER_TYPE_P(value))) {
71
+ rb_raise(rb_eTypeError, "integer argument expected");
72
+ }
73
+
74
+ int64_t integer = NUM2LL(value);
75
+ if (RB_UNLIKELY(integer < min || integer > max)) {
76
+ rb_raise(rb_eRangeError, "integer out of bounds");
77
+ }
78
+
79
+ return integer;
80
+ }
81
+
82
+ static int8_t checked_byte_value(VALUE value) {
83
+ return (int8_t)checked_integer_range(value, INT8_MIN, INT8_MAX);
84
+ }
85
+
86
+ static int16_t checked_i16_value(VALUE value) {
87
+ return (int16_t)checked_integer_range(value, INT16_MIN, INT16_MAX);
88
+ }
89
+
90
+ static int32_t checked_i32_value(VALUE value) {
91
+ return (int32_t)checked_integer_range(value, INT32_MIN, INT32_MAX);
92
+ }
93
+
94
+ static int32_t checked_size_value(VALUE value) {
95
+ return (int32_t)checked_integer_range(value, 0, INT32_MAX);
96
+ }
97
+
98
+ static int64_t checked_i64_value(VALUE value) {
99
+ return checked_integer_range(value, INT64_MIN, INT64_MAX);
100
+ }
66
101
 
67
- // TODO: implement this
68
102
  static int get_compact_type(VALUE type_value) {
69
103
  int type = FIX2INT(type_value);
70
104
  if (type == TTYPE_BOOL) {
@@ -92,10 +126,11 @@ static int get_compact_type(VALUE type_value) {
92
126
  } else if (type == TTYPE_UUID) {
93
127
  return CTYPE_UUID;
94
128
  } else {
95
- char str[50];
96
- sprintf(str, "don't know what type: %d", type);
97
- rb_raise(rb_eStandardError, "%s", str);
98
- return 0;
129
+ rb_exc_raise(get_protocol_exception(
130
+ INT2FIX(PROTOERR_INVALID_DATA),
131
+ rb_sprintf("Unknown compact type: %d", type)
132
+ ));
133
+ return 0; /* unreachable */
99
134
  }
100
135
  }
101
136
 
@@ -103,13 +138,11 @@ static void write_byte_direct(VALUE transport, int8_t b) {
103
138
  WRITE(transport, (char*)&b, 1);
104
139
  }
105
140
 
106
- static void write_field_begin_internal(VALUE self, VALUE type, VALUE id_value, VALUE type_override) {
107
- int id = FIX2INT(id_value);
141
+ static void write_field_begin_internal(VALUE self, VALUE type, int16_t id, int8_t type_override) {
142
+ int8_t type_to_write = type_override == 0 ? get_compact_type(type) : type_override;
108
143
  int last_id = LAST_ID(self);
109
144
  VALUE transport = GET_TRANSPORT(self);
110
145
 
111
- // if there's a type override, use that.
112
- int8_t type_to_write = RTEST(type_override) ? FIX2INT(type_override) : get_compact_type(type);
113
146
  // check if we can use delta encoding for the field id
114
147
  int diff = id - last_id;
115
148
  if (diff > 0 && diff <= 15) {
@@ -118,50 +151,61 @@ static void write_field_begin_internal(VALUE self, VALUE type, VALUE id_value, V
118
151
  } else {
119
152
  // write them separate
120
153
  write_byte_direct(transport, type_to_write & 0x0f);
121
- rb_thrift_compact_proto_write_i16(self, id_value);
154
+ write_varint32(transport, int_to_zig_zag(id));
122
155
  }
123
156
 
124
- SET_LAST_ID(self, id_value);
157
+ SET_LAST_ID(self, INT2FIX(id));
125
158
  }
126
159
 
127
- static int32_t int_to_zig_zag(int32_t n) {
128
- return (n << 1) ^ (n >> 31);
160
+ static uint32_t int_to_zig_zag(int32_t n) {
161
+ return (((uint32_t)n) << 1) ^ (0U - (uint32_t)(n < 0));
129
162
  }
130
163
 
131
164
  static uint64_t ll_to_zig_zag(int64_t n) {
132
- return (n << 1) ^ (n >> 63);
165
+ return (((uint64_t)n) << 1) ^ (0ULL - (uint64_t)(n < 0));
166
+ }
167
+
168
+ static uint32_t message_seqid_to_varint32(int32_t seqid) {
169
+ return seqid < 0 ? (uint32_t)((int64_t)seqid + (INT64_C(1) << 32)) : (uint32_t)seqid;
170
+ }
171
+
172
+ static int32_t message_seqid_from_varint32(uint32_t seqid) {
173
+ return seqid > INT32_MAX ? (int32_t)((int64_t)seqid - (INT64_C(1) << 32)) : (int32_t)seqid;
133
174
  }
134
175
 
135
176
  static void write_varint32(VALUE transport, uint32_t n) {
136
- while (true) {
137
- if ((n & ~0x7F) == 0) {
138
- write_byte_direct(transport, n & 0x7f);
139
- break;
140
- } else {
141
- write_byte_direct(transport, (n & 0x7F) | 0x80);
142
- n = n >> 7;
143
- }
177
+ unsigned char bytes[5];
178
+ long length = 0;
179
+
180
+ while ((n & ~0x7FU) != 0U) {
181
+ bytes[length++] = (n & 0x7FU) | 0x80U;
182
+ n >>= 7;
144
183
  }
184
+ bytes[length++] = n;
185
+
186
+ WRITE(transport, (const char*)bytes, length);
145
187
  }
146
188
 
147
189
  static void write_varint64(VALUE transport, uint64_t n) {
148
- while (true) {
149
- if ((n & ~0x7F) == 0) {
150
- write_byte_direct(transport, n & 0x7f);
151
- break;
152
- } else {
153
- write_byte_direct(transport, (n & 0x7F) | 0x80);
154
- n = n >> 7;
155
- }
190
+ unsigned char bytes[10];
191
+ long length = 0;
192
+
193
+ while ((n & ~0x7FULL) != 0ULL) {
194
+ bytes[length++] = (n & 0x7FULL) | 0x80ULL;
195
+ n >>= 7;
156
196
  }
197
+ bytes[length++] = n;
198
+
199
+ WRITE(transport, (const char*)bytes, length);
157
200
  }
158
201
 
159
202
  static void write_collection_begin(VALUE transport, VALUE elem_type, VALUE size_value) {
160
- int size = FIX2INT(size_value);
203
+ int size = checked_size_value(size_value);
204
+ int compact_type = get_compact_type(elem_type);
161
205
  if (size <= 14) {
162
- write_byte_direct(transport, size << 4 | get_compact_type(elem_type));
206
+ write_byte_direct(transport, size << 4 | compact_type);
163
207
  } else {
164
- write_byte_direct(transport, 0xf0 | get_compact_type(elem_type));
208
+ write_byte_direct(transport, 0xf0 | compact_type);
165
209
  write_varint32(transport, size);
166
210
  }
167
211
  }
@@ -208,20 +252,23 @@ VALUE rb_thrift_compact_proto_write_set_end(VALUE self) {
208
252
 
209
253
  VALUE rb_thrift_compact_proto_write_message_begin(VALUE self, VALUE name, VALUE type, VALUE seqid) {
210
254
  VALUE transport = GET_TRANSPORT(self);
255
+ uint32_t type_value = (uint32_t)FIX2INT(type);
256
+ int32_t seqid_value = checked_i32_value(seqid);
211
257
  write_byte_direct(transport, PROTOCOL_ID);
212
- write_byte_direct(transport, (VERSION & VERSION_MASK) | ((FIX2INT(type) << TYPE_SHIFT_AMOUNT) & TYPE_MASK));
213
- write_varint32(transport, FIX2INT(seqid));
258
+ write_byte_direct(transport, (VERSION & VERSION_MASK) | ((type_value << TYPE_SHIFT_AMOUNT) & TYPE_MASK));
259
+ write_varint32(transport, message_seqid_to_varint32(seqid_value));
214
260
  rb_thrift_compact_proto_write_string(self, name);
215
261
 
216
262
  return Qnil;
217
263
  }
218
264
 
219
265
  VALUE rb_thrift_compact_proto_write_field_begin(VALUE self, VALUE name, VALUE type, VALUE id) {
266
+ int16_t field_id = checked_i16_value(id);
220
267
  if (FIX2INT(type) == TTYPE_BOOL) {
221
268
  // we want to possibly include the value, so we'll wait.
222
- rb_ivar_set(self, boolean_field_id, rb_ary_new3(2, type, id));
269
+ rb_ivar_set(self, boolean_field_id, rb_ary_new3(2, type, INT2FIX(field_id)));
223
270
  } else {
224
- write_field_begin_internal(self, type, id, Qnil);
271
+ write_field_begin_internal(self, type, field_id, 0);
225
272
  }
226
273
 
227
274
  return Qnil;
@@ -233,7 +280,7 @@ VALUE rb_thrift_compact_proto_write_field_stop(VALUE self) {
233
280
  }
234
281
 
235
282
  VALUE rb_thrift_compact_proto_write_map_begin(VALUE self, VALUE ktype, VALUE vtype, VALUE size_value) {
236
- int size = FIX2INT(size_value);
283
+ int size = checked_size_value(size_value);
237
284
  VALUE transport = GET_TRANSPORT(self);
238
285
  if (size == 0) {
239
286
  write_byte_direct(transport, 0);
@@ -255,39 +302,36 @@ VALUE rb_thrift_compact_proto_write_set_begin(VALUE self, VALUE etype, VALUE siz
255
302
  }
256
303
 
257
304
  VALUE rb_thrift_compact_proto_write_bool(VALUE self, VALUE b) {
258
- int8_t type = b == Qtrue ? CTYPE_BOOLEAN_TRUE : CTYPE_BOOLEAN_FALSE;
305
+ int8_t type = RTEST(b) ? CTYPE_BOOLEAN_TRUE : CTYPE_BOOLEAN_FALSE;
259
306
  VALUE boolean_field = rb_ivar_get(self, boolean_field_id);
260
307
  if (NIL_P(boolean_field)) {
261
308
  // we're not part of a field, so just write the value.
262
309
  write_byte_direct(GET_TRANSPORT(self), type);
263
310
  } else {
264
311
  // we haven't written the field header yet
265
- write_field_begin_internal(self, rb_ary_entry(boolean_field, 0), rb_ary_entry(boolean_field, 1), INT2FIX(type));
312
+ write_field_begin_internal(self, rb_ary_entry(boolean_field, 0), FIX2INT(rb_ary_entry(boolean_field, 1)), type);
266
313
  rb_ivar_set(self, boolean_field_id, Qnil);
267
314
  }
268
315
  return Qnil;
269
316
  }
270
317
 
271
318
  VALUE rb_thrift_compact_proto_write_byte(VALUE self, VALUE byte) {
272
- CHECK_NIL(byte);
273
- write_byte_direct(GET_TRANSPORT(self), FIX2INT(byte));
319
+ write_byte_direct(GET_TRANSPORT(self), checked_byte_value(byte));
274
320
  return Qnil;
275
321
  }
276
322
 
277
323
  VALUE rb_thrift_compact_proto_write_i16(VALUE self, VALUE i16) {
278
- rb_thrift_compact_proto_write_i32(self, i16);
324
+ write_varint32(GET_TRANSPORT(self), int_to_zig_zag(checked_i16_value(i16)));
279
325
  return Qnil;
280
326
  }
281
327
 
282
328
  VALUE rb_thrift_compact_proto_write_i32(VALUE self, VALUE i32) {
283
- CHECK_NIL(i32);
284
- write_varint32(GET_TRANSPORT(self), int_to_zig_zag(NUM2INT(i32)));
329
+ write_varint32(GET_TRANSPORT(self), int_to_zig_zag(checked_i32_value(i32)));
285
330
  return Qnil;
286
331
  }
287
332
 
288
333
  VALUE rb_thrift_compact_proto_write_i64(VALUE self, VALUE i64) {
289
- CHECK_NIL(i64);
290
- write_varint64(GET_TRANSPORT(self), ll_to_zig_zag(NUM2LL(i64)));
334
+ write_varint64(GET_TRANSPORT(self), ll_to_zig_zag(checked_i64_value(i64)));
291
335
  return Qnil;
292
336
  }
293
337
 
@@ -321,8 +365,12 @@ VALUE rb_thrift_compact_proto_write_string(VALUE self, VALUE str) {
321
365
  VALUE rb_thrift_compact_proto_write_binary(VALUE self, VALUE buf) {
322
366
  buf = force_binary_encoding(buf);
323
367
  VALUE transport = GET_TRANSPORT(self);
324
- write_varint32(transport, (uint32_t)RSTRING_LEN(buf));
325
- WRITE(transport, StringValuePtr(buf), RSTRING_LEN(buf));
368
+ long size = RSTRING_LEN(buf);
369
+ if (RB_UNLIKELY(size > INT32_MAX)) {
370
+ rb_raise(rb_eRangeError, "integer out of bounds");
371
+ }
372
+ write_varint32(transport, (uint32_t)size);
373
+ WRITE(transport, StringValuePtr(buf), size);
326
374
  return Qnil;
327
375
  }
328
376
 
@@ -407,10 +455,20 @@ static int8_t get_ttype(int8_t ctype) {
407
455
  } else if (ctype == CTYPE_UUID) {
408
456
  return TTYPE_UUID;
409
457
  } else {
410
- char str[50];
411
- sprintf(str, "don't know what type: %d", ctype);
412
- rb_raise(rb_eStandardError, "%s", str);
413
- return 0;
458
+ rb_exc_raise(get_protocol_exception(
459
+ INT2FIX(PROTOERR_INVALID_DATA),
460
+ rb_sprintf("Unknown compact type: %d", ctype)
461
+ ));
462
+ return 0; /* unreachable */
463
+ }
464
+ }
465
+
466
+ static inline void validate_container_size(uint32_t size) {
467
+ if (RB_UNLIKELY(size > INT32_MAX)) {
468
+ rb_exc_raise(get_protocol_exception(
469
+ INT2FIX(PROTOERR_SIZE_LIMIT),
470
+ rb_str_new2("Container size limit exceeded")
471
+ ));
414
472
  }
415
473
  }
416
474
 
@@ -419,30 +477,57 @@ static char read_byte_direct(VALUE self) {
419
477
  return (char)(FIX2INT(byte));
420
478
  }
421
479
 
422
- static int64_t zig_zag_to_ll(int64_t n) {
423
- return (((uint64_t)n) >> 1) ^ -(n & 1);
480
+ static int64_t zig_zag_to_ll(uint64_t n) {
481
+ return (int64_t)((n >> 1) ^ (0ULL - (n & 1ULL)));
424
482
  }
425
483
 
426
- static int32_t zig_zag_to_int(int32_t n) {
427
- return (((uint32_t)n) >> 1) ^ -(n & 1);
484
+ static int32_t zig_zag_to_int(uint32_t n) {
485
+ return (int32_t)((n >> 1) ^ (0U - (n & 1U)));
428
486
  }
429
487
 
430
- static int64_t read_varint64(VALUE self) {
431
- int shift = 0;
432
- int64_t result = 0;
433
- while (true) {
488
+ #define MAX_VARINT32_BYTES 5 /* ceil(32/7); matches protobuf wire format */
489
+ #define MAX_VARINT32_LAST_BYTE 0x0f
490
+ #define MAX_VARINT64_BYTES 10 /* ceil(64/7); matches protobuf wire format */
491
+
492
+ static uint64_t read_varint64(VALUE self) {
493
+ int i, shift = 0;
494
+ uint64_t result = 0;
495
+ for (i = 0; i < MAX_VARINT64_BYTES; i++) {
434
496
  int8_t b = read_byte_direct(self);
435
- result = result | ((uint64_t)(b & 0x7f) << shift);
497
+ result |= ((uint64_t)(b & 0x7f) << shift);
436
498
  if ((b & 0x80) != 0x80) {
437
- break;
499
+ return result;
438
500
  }
439
501
  shift += 7;
440
502
  }
441
- return result;
503
+ rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_INVALID_DATA), rb_str_new2("Variable-length int over 10 bytes.")));
504
+ return 0; /* unreachable */
505
+ }
506
+
507
+ static uint32_t read_varint32(VALUE self) {
508
+ int i, shift = 0;
509
+ uint32_t result = 0;
510
+ for (i = 0; i < MAX_VARINT32_BYTES - 1; i++) {
511
+ int8_t b = read_byte_direct(self);
512
+ result |= ((uint32_t)(b & 0x7f) << shift);
513
+ if ((b & 0x80) != 0x80) {
514
+ return result;
515
+ }
516
+ shift += 7;
517
+ }
518
+
519
+ int8_t b = read_byte_direct(self);
520
+ if (RB_UNLIKELY((b & 0x80) != 0)) {
521
+ rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_INVALID_DATA), rb_str_new2("Variable-length int over 5 bytes.")));
522
+ }
523
+ if (RB_UNLIKELY((b & ~MAX_VARINT32_LAST_BYTE) != 0)) {
524
+ rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_INVALID_DATA), rb_str_new2("Variable-length int overflows uint32.")));
525
+ }
526
+ return result | ((uint32_t)b << shift);
442
527
  }
443
528
 
444
529
  static int16_t read_i16(VALUE self) {
445
- return zig_zag_to_int((int32_t)read_varint64(self));
530
+ return (int16_t)zig_zag_to_int(read_varint32(self));
446
531
  }
447
532
 
448
533
  VALUE rb_thrift_compact_proto_read_message_end(VALUE self) {
@@ -476,25 +561,27 @@ VALUE rb_thrift_compact_proto_read_set_end(VALUE self) {
476
561
  }
477
562
 
478
563
  VALUE rb_thrift_compact_proto_read_message_begin(VALUE self) {
564
+ if (RTEST(rb_ivar_get(self, reset_message_size_ivar_id))) {
565
+ rb_funcall(GET_TRANSPORT(self), reset_message_size_method_id, 0);
566
+ }
567
+
479
568
  int8_t protocol_id = read_byte_direct(self);
480
569
  if (protocol_id != PROTOCOL_ID) {
481
570
  char buf[100];
482
- int len = sprintf(buf, "Expected protocol id %d but got %d", PROTOCOL_ID, protocol_id);
483
- buf[len] = 0;
484
- rb_exc_raise(get_protocol_exception(INT2FIX(-1), rb_str_new2(buf)));
571
+ snprintf(buf, sizeof(buf), "Expected protocol id %d but got %d", PROTOCOL_ID, protocol_id);
572
+ rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_BAD_VERSION), rb_str_new2(buf)));
485
573
  }
486
574
 
487
575
  int8_t version_and_type = read_byte_direct(self);
488
576
  int8_t version = version_and_type & VERSION_MASK;
489
577
  if (version != VERSION) {
490
578
  char buf[100];
491
- int len = sprintf(buf, "Expected version id %d but got %d", version, VERSION);
492
- buf[len] = 0;
493
- rb_exc_raise(get_protocol_exception(INT2FIX(-1), rb_str_new2(buf)));
579
+ snprintf(buf, sizeof(buf), "Expected version %d but got %d", VERSION, version);
580
+ rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_BAD_VERSION), rb_str_new2(buf)));
494
581
  }
495
582
 
496
583
  int8_t type = (version_and_type >> TYPE_SHIFT_AMOUNT) & TYPE_BITS;
497
- int32_t seqid = (int32_t)read_varint64(self);
584
+ int32_t seqid = message_seqid_from_varint32(read_varint32(self));
498
585
  VALUE messageName = rb_thrift_compact_proto_read_string(self);
499
586
  return rb_ary_new3(3, messageName, INT2FIX(type), INT2NUM(seqid));
500
587
  }
@@ -532,19 +619,21 @@ VALUE rb_thrift_compact_proto_read_field_begin(VALUE self) {
532
619
  }
533
620
 
534
621
  VALUE rb_thrift_compact_proto_read_map_begin(VALUE self) {
535
- int32_t size = (int32_t)read_varint64(self);
622
+ uint32_t size = read_varint32(self);
623
+ validate_container_size(size);
536
624
  uint8_t key_and_value_type = size == 0 ? 0 : read_byte_direct(self);
537
- return rb_ary_new3(3, INT2FIX(get_ttype(key_and_value_type >> 4)), INT2FIX(get_ttype(key_and_value_type & 0xf)), INT2FIX(size));
625
+ return rb_ary_new3(3, INT2FIX(get_ttype(key_and_value_type >> 4)), INT2FIX(get_ttype(key_and_value_type & 0xf)), UINT2NUM(size));
538
626
  }
539
627
 
540
628
  VALUE rb_thrift_compact_proto_read_list_begin(VALUE self) {
541
629
  uint8_t size_and_type = read_byte_direct(self);
542
- int32_t size = (size_and_type >> 4) & 0x0f;
630
+ uint32_t size = (size_and_type >> 4) & 0x0f;
543
631
  if (size == 15) {
544
- size = (int32_t)read_varint64(self);
632
+ size = read_varint32(self);
545
633
  }
634
+ validate_container_size(size);
546
635
  uint8_t type = get_ttype(size_and_type & 0x0f);
547
- return rb_ary_new3(2, INT2FIX(type), INT2FIX(size));
636
+ return rb_ary_new3(2, INT2FIX(type), UINT2NUM(size));
548
637
  }
549
638
 
550
639
  VALUE rb_thrift_compact_proto_read_set_begin(VALUE self) {
@@ -570,7 +659,7 @@ VALUE rb_thrift_compact_proto_read_i16(VALUE self) {
570
659
  }
571
660
 
572
661
  VALUE rb_thrift_compact_proto_read_i32(VALUE self) {
573
- return INT2NUM(zig_zag_to_int((int32_t)read_varint64(self)));
662
+ return INT2NUM(zig_zag_to_int(read_varint32(self)));
574
663
  }
575
664
 
576
665
  VALUE rb_thrift_compact_proto_read_i64(VALUE self) {
@@ -580,19 +669,19 @@ VALUE rb_thrift_compact_proto_read_i64(VALUE self) {
580
669
  VALUE rb_thrift_compact_proto_read_double(VALUE self) {
581
670
  union {
582
671
  double f;
583
- int64_t l;
672
+ uint64_t l;
584
673
  } transfer;
585
674
  VALUE rbuf = rb_ivar_get(self, rbuf_ivar_id);
586
675
  rb_funcall(GET_TRANSPORT(self), read_into_buffer_method_id, 2, rbuf, INT2FIX(8));
587
- uint32_t lo = ((uint8_t)(RSTRING_PTR(rbuf)[0]))
588
- | (((uint8_t)(RSTRING_PTR(rbuf)[1])) << 8)
589
- | (((uint8_t)(RSTRING_PTR(rbuf)[2])) << 16)
590
- | (((uint8_t)(RSTRING_PTR(rbuf)[3])) << 24);
591
- uint64_t hi = (((uint8_t)(RSTRING_PTR(rbuf)[4])))
592
- | (((uint8_t)(RSTRING_PTR(rbuf)[5])) << 8)
593
- | (((uint8_t)(RSTRING_PTR(rbuf)[6])) << 16)
594
- | (((uint8_t)(RSTRING_PTR(rbuf)[7])) << 24);
595
- transfer.l = (hi << 32) | lo;
676
+ const uint8_t* bytes = (const uint8_t*)RSTRING_PTR(rbuf);
677
+ transfer.l = (uint64_t)bytes[0]
678
+ | ((uint64_t)bytes[1] << 8)
679
+ | ((uint64_t)bytes[2] << 16)
680
+ | ((uint64_t)bytes[3] << 24)
681
+ | ((uint64_t)bytes[4] << 32)
682
+ | ((uint64_t)bytes[5] << 40)
683
+ | ((uint64_t)bytes[6] << 48)
684
+ | ((uint64_t)bytes[7] << 56);
596
685
 
597
686
  return rb_float_new(transfer.f);
598
687
  }
@@ -603,8 +692,12 @@ VALUE rb_thrift_compact_proto_read_string(VALUE self) {
603
692
  }
604
693
 
605
694
  VALUE rb_thrift_compact_proto_read_binary(VALUE self) {
606
- int64_t size = read_varint64(self);
607
- return READ(self, size);
695
+ uint32_t size = read_varint32(self);
696
+ if (RB_UNLIKELY(size > INT32_MAX)) {
697
+ rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_SIZE_LIMIT), rb_str_new2("Binary size limit exceeded")));
698
+ }
699
+ CHECK_STRING_SIZE(self, size);
700
+ return rb_funcall(GET_TRANSPORT(self), read_all_method_id, 1, UINT2NUM(size));
608
701
  }
609
702
 
610
703
  VALUE rb_thrift_compact_proto_read_uuid(VALUE self) {
data/ext/constants.h CHANGED
@@ -77,14 +77,17 @@ extern ID skip_method_id;
77
77
  extern ID write_method_id;
78
78
  extern ID read_all_method_id;
79
79
  extern ID read_into_buffer_method_id;
80
+ extern ID reset_message_size_method_id;
80
81
  extern ID force_binary_encoding_id;
81
82
  extern ID convert_to_utf8_byte_buffer_id;
82
83
  extern ID convert_to_string_id;
83
84
 
84
85
  extern ID fields_const_id;
85
86
  extern ID transport_ivar_id;
87
+ extern ID reset_message_size_ivar_id;
86
88
  extern ID strict_read_ivar_id;
87
89
  extern ID strict_write_ivar_id;
90
+ extern ID max_string_size_ivar_id;
88
91
 
89
92
  extern VALUE type_sym;
90
93
  extern VALUE name_sym;
data/ext/extconf.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  #
2
3
  # Licensed to the Apache Software Foundation (ASF) under one
3
4
  # or more contributor license agreements. See the NOTICE file
@@ -17,13 +18,20 @@
17
18
  # under the License.
18
19
  #
19
20
 
20
- if defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/
21
- File.open('Makefile', 'w'){ |f| f.puts "all:\n\ninstall:\n" }
21
+ if defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
22
+ File.open("Makefile", "w") { |f| f.puts "all:\n\ninstall:\n" }
22
23
  else
23
- require 'mkmf'
24
+ require "mkmf"
24
25
 
25
26
  append_cflags(["-fsigned-char", "-g", "-O2", "-Wall", "-Werror", "-Werror=old-style-definition"])
26
27
 
28
+ # clang 21+ introduced -Wdefault-const-init-field-unsafe, which fires on
29
+ # Ruby 3.2's rstring.h (struct RString has a const field via RBasic).
30
+ # This is a Ruby header issue, not a Thrift bug, so suppress the warning here.
31
+ # append_cflags silently ignores flags unsupported by the compiler, so this
32
+ # is safe across all clang versions.
33
+ append_cflags("-Wno-default-const-init-field-unsafe")
34
+
27
35
  # Makes all symbols private by default to avoid unintended conflict
28
36
  # with other gems. To explicitly export symbols you can use RUBY_FUNC_EXPORTED
29
37
  # selectively, or entirely remove this flag.
@@ -31,5 +39,5 @@ else
31
39
 
32
40
  have_func("strlcpy", "string.h")
33
41
 
34
- create_makefile 'thrift_native'
42
+ create_makefile "thrift_native"
35
43
  end
data/ext/macros.h CHANGED
@@ -24,6 +24,17 @@
24
24
  #define CHECK_NIL(obj) if (NIL_P(obj)) { rb_raise(rb_eStandardError, "nil argument not allowed!");}
25
25
  #define READ(obj, length) rb_funcall(GET_TRANSPORT(obj), read_all_method_id, 1, INT2FIX(length))
26
26
 
27
+ // Refuse a declared string length over the protocol's @max_string_size (nil
28
+ // means no limit) before it becomes a read size.
29
+ #define CHECK_STRING_SIZE(obj, size) \
30
+ do { \
31
+ VALUE max_string_size_ = rb_ivar_get(obj, max_string_size_ivar_id); \
32
+ if (!NIL_P(max_string_size_) && RB_UNLIKELY((long long)(size) > NUM2LL(max_string_size_))) { \
33
+ rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_SIZE_LIMIT), \
34
+ rb_sprintf("String size %lld larger than the maximum %lld", (long long)(size), NUM2LL(max_string_size_)))); \
35
+ } \
36
+ } while (0)
37
+
27
38
  #ifndef RFLOAT_VALUE
28
39
  # define RFLOAT_VALUE(v) RFLOAT(rb_Float(v))->value
29
40
  #endif