teptris 0.2.52-s390x-linux → 0.2.53-s390x-linux

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ea1037536fce2900370ed6d467638588b433c8f9fe50a26eff5fb7ea0e2affdc
4
- data.tar.gz: 4027f1e1abe2e032f617faf7494bb2cba5f573f27f23f34436df6b0aacaab108
3
+ metadata.gz: 695435332d5909641a7303590f395aec50dc7d1be4cf8607df890d57c10699c1
4
+ data.tar.gz: 67bf3136a82d9e467d63687c5e93e640b76870162e39985e4dec7a578be53d30
5
5
  SHA512:
6
- metadata.gz: e0c7beecfb8a2c7d12a2305c97a8472b1391dcc7e37e4deb13470d8da19ac27e229d4a62305d42d345087cf0cf7e7da09c1baf9b1e099113d636dd172e161d2c
7
- data.tar.gz: 5252cc87f461a061d26d88dac81d6cc32c9fcb0b4f152f0aa42acc332eae65fe15cda7575558fd58a452cbc575c30e218f1856d1898ccbd6b9d554138e89913e
6
+ metadata.gz: 76d4adb38e94cd445ec22805859074c273a2371374e142a119343cb4212828636b27a5e44cb622fa53c6abf522d95460fd5112871934aef04dea920661a3d5b6
7
+ data.tar.gz: 5f77a3e4c1a3e418f6287d0b29f8aa43796b94be29ffd332f4d5986a5af079dfd389bba2e4bafb364f359aba90da195afe9bb95dd889c36afd0ac12ea77785f6
@@ -256,28 +256,6 @@ static void *parse_without_gvl(void *p)
256
256
  return NULL;
257
257
  }
258
258
 
259
- static teptris_document *parse_or_raise(VALUE str) {
260
- teptris_document *doc = NULL;
261
- /* frozen strings cannot take the tmp lock (ruby 4.0 raises
262
- * FrozenError - chilled literals); a dup is movable-page-safe the
263
- * same way, at one input copy for frozen callers only */
264
- volatile VALUE pin = OBJ_FROZEN(str) ? rb_str_dup(str) : str;
265
- rb_str_locktmp(pin);
266
- struct gvl_parse_arg arg = {RSTRING_PTR(pin), (size_t)RSTRING_LEN(pin),
267
- &doc, TEPTRIS_OK};
268
- rb_thread_call_without_gvl(parse_without_gvl, &arg, RUBY_UBF_IO, NULL);
269
- rb_str_unlocktmp(pin);
270
- teptris_status st = arg.st;
271
- if (st != TEPTRIS_OK) {
272
- const teptris_error *e = teptris_document_error(doc);
273
- VALUE ex = rb_exc_new(eParseError, e->message, (long)strlen(e->message));
274
- rb_iv_set(ex, "@line", SIZET2NUM(e->line));
275
- rb_iv_set(ex, "@column", SIZET2NUM(e->column));
276
- teptris_document_free(doc);
277
- rb_exc_raise(ex);
278
- }
279
- return doc;
280
- }
281
259
 
282
260
  static VALUE ext_load(int argc, VALUE *argv, VALUE self) {
283
261
  VALUE str, opts;
@@ -292,11 +270,29 @@ static VALUE ext_load(int argc, VALUE *argv, VALUE self) {
292
270
  if (RTEST(ft)) flags |= FMT_FORBID_TIME;
293
271
  if (RTEST(fd)) flags |= FMT_FORBID_DATE;
294
272
  }
295
- teptris_document *doc = parse_or_raise(str);
273
+ /* the parse is pure C over a private copy, GVL-free; the copy
274
+ * must outlive materialization - the document's strings are
275
+ * zero-copy views into it (a real UAF on glibc when freed early) */
276
+ long len = RSTRING_LEN(str);
277
+ char *cbuf = ALLOC_N(char, (size_t)len + 1);
278
+ memcpy(cbuf, RSTRING_PTR(str), (size_t)len);
279
+ teptris_document *doc = NULL;
280
+ struct gvl_parse_arg arg = {cbuf, (size_t)len, &doc, TEPTRIS_OK};
281
+ rb_thread_call_without_gvl(parse_without_gvl, &arg, RUBY_UBF_IO, NULL);
282
+ if (arg.st != TEPTRIS_OK) {
283
+ const teptris_error *e = teptris_document_error(doc);
284
+ VALUE ex = rb_exc_new(eParseError, e->message, (long)strlen(e->message));
285
+ rb_iv_set(ex, "@line", SIZET2NUM(e->line));
286
+ rb_iv_set(ex, "@column", SIZET2NUM(e->column));
287
+ xfree(cbuf);
288
+ teptris_document_free(doc);
289
+ rb_exc_raise(ex);
290
+ }
296
291
  keycache kc;
297
292
  kc_init(&kc);
298
293
  VALUE out = obj_from_node(teptris_document_root(doc), flags, &kc);
299
294
  kc_free(&kc);
295
+ xfree(cbuf);
300
296
  teptris_document_free(doc);
301
297
  return out;
302
298
  }
@@ -741,7 +737,8 @@ static VALUE cLazyOwner, cLazyValue;
741
737
 
742
738
  typedef struct {
743
739
  teptris_document *doc;
744
- VALUE input; /* the borrowed views' buffer */
740
+ VALUE input; /* single-doc path: owned frozen copy (GC-marked) */
741
+ char *buf; /* batch path: owned input copy the doc's views use */
745
742
  } lazy_owner;
746
743
 
747
744
  typedef struct {
@@ -752,12 +749,18 @@ typedef struct {
752
749
  static void lazy_owner_free(void *p) {
753
750
  lazy_owner *o = (lazy_owner *)p;
754
751
  if (o->doc != NULL) teptris_document_free(o->doc);
752
+ if (o->buf != NULL) xfree(o->buf);
755
753
  xfree(o);
756
754
  }
757
755
  static size_t lazy_owner_size(const void *p) { return sizeof(lazy_owner); }
756
+ static void lazy_owner_mark(void *p)
757
+ {
758
+ lazy_owner *o = (lazy_owner *)p;
759
+ if (o->input) rb_gc_mark(o->input); /* roots the single-path copy */
760
+ }
758
761
  static const rb_data_type_t lazy_owner_type = {
759
762
  "TeptrisExt/LazyOwner",
760
- {NULL, lazy_owner_free, lazy_owner_size, NULL, 0},
763
+ {lazy_owner_mark, lazy_owner_free, lazy_owner_size, NULL, 0},
761
764
  NULL, NULL, RUBY_TYPED_FREE_IMMEDIATELY};
762
765
 
763
766
  static void lazy_value_mark(void *p) {
@@ -792,7 +795,7 @@ static VALUE ext_load_lazy(VALUE self, VALUE toml) {
792
795
  VALUE owner = TypedData_Make_Struct(cLazyOwner, lazy_owner,
793
796
  &lazy_owner_type, o);
794
797
  o->doc = NULL;
795
- o->input = rb_str_new_frozen(toml); /* borrowed views: own a copy */
798
+ o->input = rb_str_new_frozen(toml); /* owned frozen copy */
796
799
  teptris_status st = teptris_parse(RSTRING_PTR(o->input),
797
800
  (size_t)RSTRING_LEN(o->input),
798
801
  NULL, &o->doc);
@@ -973,25 +976,53 @@ static unsigned flags_from_opts(VALUE opts) {
973
976
  /* Parse-only batch (returns an Array of Lazy wrappers, one per doc):
974
977
  * eager parse, deferred materialization. Same per-doc error semantics
975
978
  * as ext_load_lazy. */
979
+ struct batch_parse_arg {
980
+ const char **data;
981
+ size_t *lens;
982
+ size_t n;
983
+ teptris_document **docs;
984
+ teptris_status *statuses;
985
+ teptris_status batch;
986
+ };
987
+
988
+ static void *batch_parse_without_gvl(void *p)
989
+ {
990
+ struct batch_parse_arg *a = (struct batch_parse_arg *)p;
991
+ a->batch = teptris_parse_batch(a->data, a->lens, a->n, NULL,
992
+ a->docs, a->statuses);
993
+ return NULL;
994
+ }
995
+
976
996
  static VALUE ext_load_lazy_batch(VALUE self, VALUE strings) {
977
997
  Check_Type(strings, T_ARRAY);
978
998
  long n = RARRAY_LEN(strings);
979
999
  if (n == 0) return rb_ary_new();
980
1000
  batch_scratch *s = batch_scratch_alloc(n);
981
1001
  if (s == NULL) rb_raise(eError, "out of memory batching %ld documents", n);
1002
+ /* the batch parse is pure C: pin every input (one tmp lock per
1003
+ * string - repeated objects dup; frozen strings cannot take the
1004
+ * lock, yet compaction can move them) and release the GVL for the
1005
+ * whole parse */
1006
+ /* the batch parse is pure C over private copies: a Ruby-string
1007
+ * buffer can move under a concurrent compacting GC, and locktmp
1008
+ * does not pin on every platform - copies are C memory, immovable
1009
+ * by construction. Each LazyOwner owns its copy (freed with the
1010
+ * owner): the lazy nodes view these buffers until materialized. */
1011
+ char **bufs = ALLOC_N(char *, n);
982
1012
  for (long i = 0; i < n; i++) {
983
1013
  VALUE str = rb_ary_entry(strings, i);
984
1014
  StringValue(str); /* raises TypeError if not coercible */
985
- s->data[i] = RSTRING_PTR(str);
986
- s->lens[i] = (size_t)RSTRING_LEN(str);
987
- }
988
- teptris_status batch =
989
- teptris_parse_batch(s->data, s->lens, (size_t)n, NULL,
990
- s->docs, s->statuses);
991
- if (batch != TEPTRIS_OK) {
992
- batch_scratch_free(s);
993
- rb_raise(eError, "batch parse failed (status %d)", (int)batch);
1015
+ long len = RSTRING_LEN(str);
1016
+ bufs[i] = ALLOC_N(char, (size_t)len + 1);
1017
+ memcpy(bufs[i], RSTRING_PTR(str), (size_t)len);
1018
+ s->data[i] = bufs[i];
1019
+ s->lens[i] = (size_t)len;
994
1020
  }
1021
+ struct batch_parse_arg arg = {s->data, s->lens, (size_t)n,
1022
+ s->docs, s->statuses, TEPTRIS_OK};
1023
+ rb_thread_call_without_gvl(batch_parse_without_gvl, &arg,
1024
+ RUBY_UBF_IO, NULL);
1025
+ teptris_status batch = arg.batch;
995
1026
  long first_fail = -1;
996
1027
  for (long i = 0; i < n; i++) {
997
1028
  if (s->statuses[i] != TEPTRIS_OK && first_fail == -1) first_fail = i;
@@ -1012,7 +1043,7 @@ static VALUE ext_load_lazy_batch(VALUE self, VALUE strings) {
1012
1043
  VALUE owner = TypedData_Make_Struct(cLazyOwner, lazy_owner,
1013
1044
  &lazy_owner_type, o);
1014
1045
  o->doc = s->docs[i];
1015
- o->input = rb_ary_entry(strings, i);
1046
+ o->buf = bufs[i]; /* the owner owns this copy */
1016
1047
  rb_ary_push(out, lazy_wrap(owner, teptris_document_root(s->docs[i])));
1017
1048
  }
1018
1049
  batch_scratch_free(s);
@@ -1053,35 +1084,29 @@ static VALUE ext_load_slice(int argc, VALUE *argv, VALUE self) {
1053
1084
  if (n == 0) return rb_ary_new();
1054
1085
  unsigned flags = flags_from_opts(opts);
1055
1086
 
1056
- volatile VALUE *pins = ALLOC_N(VALUE, n);
1087
+ /* the GVL-free parse reads private copies: a Ruby-string buffer
1088
+ * can move under a concurrent compacting GC, and locktmp does not
1089
+ * pin on every platform (windows ruby 3.3 parsed garbage). Copies
1090
+ * are C memory - immovable by construction. Freed after
1091
+ * materialization (the docs view them until then). */
1057
1092
  const char **data = ALLOC_N(const char *, n);
1058
1093
  size_t *lens = ALLOC_N(size_t, n);
1059
1094
  teptris_document **docs = ALLOC_N(teptris_document *, n);
1060
1095
  teptris_status *stats = ALLOC_N(teptris_status, n);
1061
- /* the tmp lock is one-per-string: repeated objects in the slice
1062
- * get a dup before pinning (frozen inputs dup for the same reason
1063
- * - they cannot take the lock, yet compaction can move them) */
1064
- VALUE seen = rb_hash_new();
1096
+ char **bufs = ALLOC_N(char *, n);
1065
1097
  long i;
1066
1098
  for (i = 0; i < n; i++) {
1067
1099
  VALUE str = rb_ary_entry(strs, i);
1068
1100
  StringValue(str);
1069
- VALUE id = rb_obj_id(str);
1070
- if (!NIL_P(rb_hash_aref(seen, id))) {
1071
- str = rb_str_dup(str);
1072
- } else {
1073
- rb_hash_aset(seen, id, Qtrue);
1074
- if (OBJ_FROZEN(str)) str = rb_str_dup(str);
1075
- }
1076
- pins[i] = str;
1077
- rb_str_locktmp(pins[i]);
1078
- data[i] = RSTRING_PTR(pins[i]);
1079
- lens[i] = (size_t)RSTRING_LEN(pins[i]);
1101
+ long len = RSTRING_LEN(str);
1102
+ bufs[i] = ALLOC_N(char, (size_t)len + 1);
1103
+ memcpy(bufs[i], RSTRING_PTR(str), (size_t)len);
1104
+ data[i] = bufs[i];
1105
+ lens[i] = (size_t)len;
1080
1106
  }
1081
1107
  struct slice_parse_arg arg = {n, data, lens, docs, stats};
1082
1108
  rb_thread_call_without_gvl(slice_parse_without_gvl, &arg,
1083
1109
  RUBY_UBF_IO, NULL);
1084
- for (i = 0; i < n; i++) rb_str_unlocktmp(pins[i]);
1085
1110
 
1086
1111
  VALUE out = rb_ary_new_capa(n);
1087
1112
  keycache kc;
@@ -1091,7 +1116,7 @@ static VALUE ext_load_slice(int argc, VALUE *argv, VALUE self) {
1091
1116
  const char *msg = "parse failed (out of memory)";
1092
1117
  VALUE ex = rb_exc_new(eParseError, msg, (long)strlen(msg));
1093
1118
  kc_free(&kc);
1094
- xfree(pins); xfree(data); xfree(lens); xfree(docs); xfree(stats);
1119
+ xfree(data); xfree(lens); xfree(docs); xfree(stats);
1095
1120
  rb_exc_raise(ex);
1096
1121
  }
1097
1122
  const teptris_error *e = teptris_document_error(docs[i]);
@@ -1103,7 +1128,7 @@ static VALUE ext_load_slice(int argc, VALUE *argv, VALUE self) {
1103
1128
  long j;
1104
1129
  for (j = i; j < n; j++) teptris_document_free(docs[j]);
1105
1130
  kc_free(&kc);
1106
- xfree(pins); xfree(data); xfree(lens); xfree(docs); xfree(stats);
1131
+ xfree(data); xfree(lens); xfree(docs); xfree(stats);
1107
1132
  rb_exc_raise(ex);
1108
1133
  }
1109
1134
  }
@@ -1113,7 +1138,7 @@ static VALUE ext_load_slice(int argc, VALUE *argv, VALUE self) {
1113
1138
  teptris_document_free(docs[i]);
1114
1139
  }
1115
1140
  kc_free(&kc);
1116
- xfree(pins); xfree(data); xfree(lens); xfree(docs); xfree(stats);
1141
+ xfree(data); xfree(lens); xfree(docs); xfree(stats);
1117
1142
  return out;
1118
1143
  }
1119
1144
 
Binary file
Binary file
Binary file
@@ -1,3 +1,3 @@
1
1
  module Teptris
2
- VERSION = "0.2.52"
2
+ VERSION = "0.2.53"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: teptris
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.52
4
+ version: 0.2.53
5
5
  platform: s390x-linux
6
6
  authors:
7
7
  - teptris contributors