Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory access violation / Crash during GC #1985

Closed
aleksandrs-ledovskis opened this issue Feb 2, 2020 · 15 comments
Closed

Memory access violation / Crash during GC #1985

aleksandrs-ledovskis opened this issue Feb 2, 2020 · 15 comments

Comments

@aleksandrs-ledovskis
Copy link

@aleksandrs-ledovskis aleksandrs-ledovskis commented Feb 2, 2020

Was tasked with fixing crash in one production application that was pointing to Nokogiri.
Two day's worth of debugging resulted in following.

My understanding of the issue

Check relevant # --N-- markers in dehydrated reproduction code.

--1--

XML schema document is parsed and by Nokogiri as Libxml2's xmlDoc.

--2--

xsd_doc.first_element_child.children.to_a generates a xmlNodeSet with bunch of xmlNode's, including one with node->type == XML_TEXT_NODE and node->content == '\n'.

--3--

Schema::from_document executes xmlSchemaParse in Libxml2 xmlschemas.c which
calls

	/*
	* Locate and add the schema document.
	*/
	res = xmlSchemaAddSchemaDoc(ctxt, XML_SCHEMA_SCHEMA_MAIN,
	ctxt->URL, ctxt->doc, ctxt->buffer, ctxt->size, NULL,
	NULL, NULL, &bucket);

and later

	/*
	* Remove all the blank text nodes.
	*/
	xmlSchemaCleanupDoc(pctxt, docElem);

The xmlNode representing '\n' is flushed away using xmlFree (ruby_xfree).

--4--

A GC pressure is built up by doing some random array generation. One of RData VALUEs will almost certainly hit place occupied by pruned xmlNode.

During on of array capacity increases, a GC is auto-started which executes push_mark_stack in gc.c with one of data being a RData pointing to xmlNode

image

This is direct cause of crash.

While GC runs mark & sweep routines, during one of iterations of which, the gc_mark_stacked_objects to pop_mark_stack call is done which returns RData which has been pushed on mark stack. It is detected as T_DATA with custom dmark (mark in Nokogiri's xml_node.c).

As node was free'd and its memory was reused by others objects, xmlDocPtr doc = node->doc; returns inaccessible memory location. Any attempt to use it down the run yields segfault.

To Reproduce

#! /usr/bin/env ruby

require 'nokogiri'

no_crash_doc = <<~doc
<?xml version="1.0" encoding="UTF-8" ?><xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"><xs:element name="foo" type="xs:string"/></xs:schema>
doc

crash_doc = <<~doc
<?xml version="1.0" encoding="UTF-8" ?><xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="foo" type="xs:string"/></xs:schema>
doc

body = lambda do |doc|
  # --1--
  xsd_doc = Nokogiri::XML(doc)

  # --2--
  # #to_a is crash culprit
  # Commenting this line/removing array conversion will make crash go away
  xsd_doc.first_element_child.children.to_a

  # --3--
  Nokogiri::XML::Schema.from_document(xsd_doc)

  # --4--
  # This forces population of heap with junky stuff, some of which will
  # leak into place that held freed empty-string xmlNode
  [1,2,3,4,5].repeated_permutation(8).to_a
end

# Testing 'no crash' scenario is not required for crash to occur
# It just shows that single \n variation causes the memory access violation
puts "Testing schema without newline XML_TEXT_NODE / Press any key"
STDIN.gets

body.call(no_crash_doc)

GC.start # Semi clean slate

puts "Testing schema with newline XML_TEXT_NODE / Press any key"
puts "Time is ripe to attach debugger to PID: #{Process.pid}"
STDIN.gets

body.call(crash_doc)

Environment

master nokogiri and bundled libxml2/libxslt, compiled with -O0 (it was a royal pain to get working)

Full C level backtrace

-- C level backtrace information -------------------------------------------
0   ruby                                0x000000010e5b3969 rb_print_backtrace + 25
1   ruby                                0x000000010e5b3a78 rb_vm_bugreport + 136
2   ruby                                0x000000010e3a1872 rb_bug_context + 450
3   ruby                                0x000000010e4f5f68 sigsegv + 88
4   libsystem_platform.dylib            0x00007fff75bf4f5a _sigtramp + 26
5   nokogiri.bundle                     0x000000010e95204c mark + 28
6   ruby                                0x000000010e3c81e4 gc_mark_children + 1092
7   ruby                                0x000000010e3d022c gc_mark_stacked_objects + 108
8   ruby                                0x000000010e3d093b gc_mark_stacked_objects_all + 27
9   ruby                                0x000000010e3cfb91 gc_marks_rest + 129
10  ruby                                0x000000010e3d1667 gc_marks + 103
11  ruby                                0x000000010e3ce1c2 gc_start + 802
12  ruby                                0x000000010e3c5718 garbage_collect + 56
13  ruby                                0x000000010e3d4ec5 garbage_collect_with_gvl + 101
14  ruby                                0x000000010e3c9360 objspace_malloc_increase + 320
15  ruby                                0x000000010e3d5012 objspace_malloc_fixup + 66
16  ruby                                0x000000010e3c8ba6 objspace_xmalloc0 + 150
17  ruby                                0x000000010e3c8afc ruby_xmalloc2 + 60
18  ruby                                0x000000010e30ba94 ary_new + 260
19  ruby                                0x000000010e30b97f rb_ary_new_capa + 31
20  ruby                                0x000000010e32047d yield_indexed_values + 29
21  ruby                                0x000000010e3209a9 rpermute0 + 169
22  ruby                                0x000000010e31b630 rb_ary_repeated_permutation + 512
23  ruby                                0x000000010e5a83ca call_cfunc_1 + 42
24  ruby                                0x000000010e5aa1d8 vm_call0_cfunc_with_frame + 568
25  ruby                                0x000000010e5a9f95 vm_call0_cfunc + 53
26  ruby                                0x000000010e5a9c11 vm_call0_body + 401
27  ruby                                0x000000010e58d279 vm_call0 + 121
28  ruby                                0x000000010e5aaaaf rb_call0 + 159
29  ruby                                0x000000010e58e150 rb_call + 80
30  ruby                                0x000000010e58f18d iterate_method + 61
31  ruby                                0x000000010e58efc0 rb_iterate0 + 288
32  ruby                                0x000000010e58ee97 rb_iterate + 119
33  ruby                                0x000000010e58f13b rb_block_call + 91
34  ruby                                0x000000010e39d0a6 enumerator_block_call + 150
35  ruby                                0x000000010e39b6b8 enumerator_each + 216
36  ruby                                0x000000010e5a836a call_cfunc_m1 + 42
37  ruby                                0x000000010e5aa1d8 vm_call0_cfunc_with_frame + 568
38  ruby                                0x000000010e5a9f95 vm_call0_cfunc + 53
39  ruby                                0x000000010e5a9c11 vm_call0_body + 401
40  ruby                                0x000000010e58d279 vm_call0 + 121
41  ruby                                0x000000010e5aaaaf rb_call0 + 159
42  ruby                                0x000000010e58e150 rb_call + 80
43  ruby                                0x000000010e58f18d iterate_method + 61
44  ruby                                0x000000010e58efc0 rb_iterate0 + 288
45  ruby                                0x000000010e58ee97 rb_iterate + 119
46  ruby                                0x000000010e58f13b rb_block_call + 91
47  ruby                                0x000000010e38a6cb enum_to_a + 75
48  ruby                                0x000000010e5a836a call_cfunc_m1 + 42
49  ruby                                0x000000010e59dc5d vm_call_cfunc_with_frame + 605
50  ruby                                0x000000010e59936d vm_call_cfunc + 173
51  ruby                                0x000000010e58416e vm_exec_core + 8974
52  ruby                                0x000000010e593ed6 vm_exec + 182
53  ruby                                0x000000010e5ab0ef invoke_block + 223
54  ruby                                0x000000010e5aaf5f invoke_iseq_block_from_c + 431
55  ruby                                0x000000010e5ac7a3 invoke_block_from_c_proc + 163
56  ruby                                0x000000010e592f7b vm_invoke_proc + 235
57  ruby                                0x000000010e592d80 rb_vm_invoke_proc + 128
58  ruby                                0x000000010e59a25c vm_call_opt_call + 348
59  ruby                                0x000000010e598b7d vm_call_method_each_type + 1069
60  ruby                                0x000000010e5985a4 vm_call_method + 148
61  ruby                                0x000000010e598505 vm_call_general + 53
62  ruby                                0x000000010e58416e vm_exec_core + 8974
63  ruby                                0x000000010e593ed6 vm_exec + 182
64  ruby                                0x000000010e594c5b rb_iseq_eval_main + 43
65  ruby                                0x000000010e3acb78 ruby_exec_internal + 232
66  ruby                                0x000000010e3aca81 ruby_exec_node + 33
67  ruby                                0x000000010e3aca40 ruby_run_node + 64
68  ruby                                0x000000010e30ac61 main + 113
@flavorjones
Copy link
Member

@flavorjones flavorjones commented Feb 3, 2020

@aleksandrs-ledovskis Thanks for opening this issue, and sorry you're experiencing this problem.

I'll take a deeper look at this over the next few days.

@tenderlove
Copy link
Member

@tenderlove tenderlove commented Feb 3, 2020

I couldn't reproduce with Ruby, but the explanation makes sense. I think the XSLT parser shouldn't touch memory that it didn't allocate. Anyway, I was able to write a C program that uses libxml2 to demonstrate the problem:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <libxml/parser.h>
#include <libxml/xmlschemas.h>

static xmlNode *
find_text_node(xmlNode * a_node)
{
  xmlNode *cur_node = NULL;

  for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
    if (cur_node->type == XML_TEXT_NODE) {
      return cur_node;
    } else {
      xmlNode *found = find_text_node(cur_node->children);

      if (found) {
        return found;
      }
    }
  }

  return NULL;
}

int main(int argc, char *argv[]) {
  char * source = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\n<xs:element name=\"foo\" type=\"xs:string\"/></xs:schema>";

  xmlDocPtr doc = xmlReadMemory(source, strlen(source), NULL, NULL, 0);
  xmlNodePtr root = xmlDocGetRootElement(doc);
  xmlSchemaParserCtxtPtr ctx;
  xmlSchemaPtr schema;

  xmlNode * text = find_text_node(root);
  printf("node type: Element, name: %s %d\n", text->name, xmlIsBlankNode(text));

  ctx = xmlSchemaNewDocParserCtxt(doc);
  schema = xmlSchemaParse(ctx);

  printf("node type: Element, name: %s %d\n", text->name, xmlIsBlankNode(text));

  exit(0);
}

If I enable malloc scribbling on MacOS, it will segv:

$ env MallocScribble=1 ./a.out
a.out(78495,0x102687dc0) malloc: enabling scribbling to detect mods to free blocks
node type: Element, name: text 1
fish: 'env MallocScribble=1 ./a.out' terminated by signal SIGSEGV (Address boundary error)

I think upstream should probably not call that free function when a schema is created from a document.

@aleksandrs-ledovskis
Copy link
Author

@aleksandrs-ledovskis aleksandrs-ledovskis commented Feb 4, 2020

@tenderlove Thanks for taking a time to investigate and building a C repro program.

It is somewhat strange that you couldn't repeat the crash. While I was testing initially with CRuby 2.5.7 (to match with PROD version), I have just retested with master.

ruby 2.8.0dev (2020-02-04T05:41:52Z master 9cdc964d07) [x86_64-darwin17]

Env and build chain (if that matters):

Apple LLVM version 10.0.0 (clang-1000.11.45.5)
17.7.0 Darwin Kernel Version 17.7.0: Thu Jan 23 07:05:23 PST 2020; root:xnu-4570.71.69~1/RELEASE_X86_64 x86_64
PhysMem: 16G used (2902M wired), 148M unused

Interestingly enough, 2.8.0 crashed less (3 out of 5 runs). But the traces still pointed out to same
source (mark in xmlnode.c). Bumping a number of array permutations ([1,2,3,4,5].repeated_permutation(9).to_a) yielded a positive increase in crash-iness (10/10).


Regarding your note that this looks like a problem in upstream/Libxml2, their "official guideline" is quite involved (and seems outdated), so I would like to ask @nwellnhof to confirm if mailing list is still the way to go or reporting on GitLab instance (https://gitlab.gnome.org/GNOME/libxml2/issues) is sufficient?

Nokogiri is using Libxml2 v2.9.10 and Libxslt v1.1.34

@nwellnhof
Copy link

@nwellnhof nwellnhof commented Feb 4, 2020

You can report bugs either on the libxml2 GitLab instance or the mailing list.

Regarding this issue, xmlSchemaParse removes blank text nodes from the input document via xmlSchemaCleanupDoc by design. Unfortunately, it seems hard to work around or fix this mistake.

@aleksandrs-ledovskis
Copy link
Author

@aleksandrs-ledovskis aleksandrs-ledovskis commented Feb 4, 2020

If per Nick's comment it's unfeasible to change the way how Libxml2 operates, maybe at least Nokogiri could detect (and raise hell) in situations when document containing blank text objects is passed on to Schema::from_document?

I would prefer to know right away that unsafe operation is attempted rather than to come back to crashed app's carcass.

@tenderlove
Copy link
Member

@tenderlove tenderlove commented Feb 4, 2020

It is somewhat strange that you couldn't repeat the crash. While I was testing initially with CRuby 2.5.7 (to match with PROD version), I have just retested with master.

tbf, I only ran the Ruby program twice. Your description was clear enough and reading the code made it obvious there's a bug. I figured we'd need a pure C test case anyway if we plan to fix upstream.

I will try to think of a fix for libxml2, but even if I can't I'll still file a ticket upstream.

If per Nick's comment it's unfeasible to change the way how Libxml2 operates, maybe at least Nokogiri could detect (and raise hell) in situations when document containing blank text objects is passed on to Schema::from_document?

I think this is a possible alternative. The document should know what nodes have been exposed to Ruby, and if any of them are "blank", we can raise an exception. It'll make constructing the schema a little more expensive, but I agree it's better to get an exception than find a segv later.

@tenderlove
Copy link
Member

@tenderlove tenderlove commented Feb 4, 2020

Probably needs a better exception, but this would prevent the segvs:

diff --git a/ext/nokogiri/xml_schema.c b/ext/nokogiri/xml_schema.c
index da2774ba..42bbeb8d 100644
--- a/ext/nokogiri/xml_schema.c
+++ b/ext/nokogiri/xml_schema.c
@@ -133,6 +133,31 @@ static VALUE read_memory(VALUE klass, VALUE content)
   return rb_schema;
 }
 
+/* Schema creation will remove and deallocate "blank" nodes.
+ * If those blank nodes have been exposed to Ruby, they could get freed
+ * out from under the VALUE pointer.  This function checks to see if any of
+ * those nodes have been exposed to Ruby, and if so we should raise an exception.
+ */
+static int has_blank_nodes_p(VALUE cache)
+{
+    long i;
+
+    if (NIL_P(cache)) {
+        return 0;
+    }
+
+    for (i = 0; i < RARRAY_LEN(cache); i++) {
+        xmlNodePtr node;
+        VALUE element = rb_ary_entry(cache, i);
+        Data_Get_Struct(element, xmlNode, node);
+        if (xmlIsBlankNode(node)) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
 /*
  * call-seq:
  *  from_document(doc)
@@ -152,6 +177,10 @@ static VALUE from_document(VALUE klass, VALUE document)
   /* In case someone passes us a node. ugh. */
   doc = doc->doc;
 
+  if (has_blank_nodes_p(DOC_NODE_CACHE(doc))) {
+    rb_raise(rb_eRuntimeError, "Creating a schema from a document that has blank nodes exposed to Ruby is dangerous");
+  }
+
   ctx = xmlSchemaNewDocParserCtxt(doc);
 
   errors = rb_ary_new();
@tenderlove
Copy link
Member

@tenderlove tenderlove commented Feb 4, 2020

Something I think is interesting is that you can pretty easily demonstrate that turning a document in to a schema removes the node:

require 'nokogiri'

doc = <<~doc
<?xml version="1.0" encoding="UTF-8" ?><xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="foo" type="xs:string"/></xs:schema>
doc

xsd_doc = Nokogiri::XML(doc)
p xsd_doc.root.children.find(&:blank?) # Finds a node

xsd_doc = Nokogiri::XML(doc)
Nokogiri::XML::Schema.from_document(xsd_doc)
p xsd_doc.root.children.find(&:blank?) # nil

Also this will reliably SEGV with MallocScribble:

require 'nokogiri'

doc = <<~doc
<?xml version="1.0" encoding="UTF-8" ?><xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="foo" type="xs:string"/></xs:schema>
doc

xsd_doc = Nokogiri::XML(doc)
node = xsd_doc.root.children.find(&:blank?) # Finds a node

Nokogiri::XML::Schema.from_document(xsd_doc)

p node.empty?
[aaron@TC-2 ~/g/nokogiri (master)]$ env MallocScribble=1 ruby thing.rb
env(82207,0x11390edc0) malloc: enabling scribbling to detect mods to free blocks
bash(82207,0x111141dc0) malloc: enabling scribbling to detect mods to free blocks
env(82207,0x114e7ddc0) malloc: enabling scribbling to detect mods to free blocks
bash(82207,0x10e2b9dc0) malloc: enabling scribbling to detect mods to free blocks
env(82207,0x10b158dc0) malloc: enabling scribbling to detect mods to free blocks
bash(82207,0x11ad4bdc0) malloc: enabling scribbling to detect mods to free blocks
env(82211,0x108e08dc0) malloc: enabling scribbling to detect mods to free blocks
bash(82211,0x118ce5dc0) malloc: enabling scribbling to detect mods to free blocks
env(82212,0x11669cdc0) malloc: enabling scribbling to detect mods to free blocks
bash(82212,0x113548dc0) malloc: enabling scribbling to detect mods to free blocks
env(82213,0x1178f1dc0) malloc: enabling scribbling to detect mods to free blocks
bash(82213,0x10f057dc0) malloc: enabling scribbling to detect mods to free blocks
env(82214,0x10f097dc0) malloc: enabling scribbling to detect mods to free blocks
bash(82214,0x114859dc0) malloc: enabling scribbling to detect mods to free blocks
env(82215,0x10b210dc0) malloc: enabling scribbling to detect mods to free blocks
bash(82215,0x109f20dc0) malloc: enabling scribbling to detect mods to free blocks
ruby(82207,0x1053f7dc0) malloc: enabling scribbling to detect mods to free blocks
/Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/spell_checker.rb:44: [BUG] Segmentation fault at 0x0000000000000000
ruby 2.6.4p104 (2019-08-28 revision 67798) [x86_64-darwin18]

-- Crash Report log information --------------------------------------------
   See Crash Report log file under the one of following:                    
     * ~/Library/Logs/DiagnosticReports                                     
     * /Library/Logs/DiagnosticReports                                      
   for more details.                                                        
Don't forget to include the above Crash Report log file in bug reports.     

-- Control frame information -----------------------------------------------
c:0010 p:---- s:0048 e:000047 CFUNC  :to_s
c:0009 p:0026 s:0044 e:000043 METHOD /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/spell_checker.rb:44
c:0008 p:0014 s:0038 e:000036 BLOCK  /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/spell_checker.rb:16 [FINISH]
c:0007 p:---- s:0033 e:000032 CFUNC  :select
c:0006 p:0035 s:0029 e:000028 METHOD /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/spell_checker.rb:16
c:0005 p:0045 s:0021 e:000020 METHOD /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/spell_checkers/method_name_chec
c:0004 p:0021 s:0017 e:000016 METHOD /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/core_ext/name_error.rb:20
c:0003 p:0052 s:0013 e:000010 METHOD /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/core_ext/name_error.rb:11 [FINISH]
c:0002 p:---- s:0006 e:000005 CFUNC  :message
c:0001 p:0000 s:0003 E:000b50 (none) [FINISH]

-- Ruby level backtrace information ----------------------------------------
thing.rb:0:in `message'
/Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/core_ext/name_error.rb:11:in `to_s'
/Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/core_ext/name_error.rb:20:in `corrections'
/Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/spell_checkers/method_name_checker.rb:46:in `corrections'
/Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/spell_checker.rb:16:in `correct'
/Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/spell_checker.rb:16:in `select'
/Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/spell_checker.rb:16:in `block in correct'
/Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/spell_checker.rb:44:in `normalize'
/Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/spell_checker.rb:44:in `to_s'

-- Machine register context ------------------------------------------------
 rax: 0xaaaaaaaaaaaaaaaa rbx: 0x00007ff25e8424e0 rcx: 0x4deea977bd5600e5
 rdx: 0x6dabef7df7ffbefb rdi: 0x00007ff260b718a0 rsi: 0x00007ff26010d9e8
 rbp: 0x00007ffeeefc8a50 rsp: 0x00007ffeeefc8a50  r8: 0x00007ff25e55db30
  r9: 0x0000000000000000 r10: 0x0000000000000000 r11: 0x0000000101157e08
 r12: 0x0000000000000000 r13: 0x00007ff25e840000 r14: 0x0000000000000001
 r15: 0x00007ff25e502f00 rip: 0x0000000103230c18 rfl: 0x0000000000010206

-- C level backtrace information -------------------------------------------
/Users/aaron/.rbenv/versions/2.6.4/bin/ruby(rb_vm_bugreport+0x82) [0x100e76fc2]
/Users/aaron/.rbenv/versions/2.6.4/bin/ruby(0x100cc1d93) [0x100cc1d93]
/Users/aaron/.rbenv/versions/2.6.4/bin/ruby(0x100ddb181) [0x100ddb181]
/usr/lib/system/libsystem_platform.dylib(_sigtramp+0x1d) [0x7fff656ec42d]
/Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/nokogiri.bundle(mark+0x8) [0x103230c18]
/Users/aaron/.rbenv/versions/2.6.4/bin/ruby(0x100ce9e71) [0x100ce9e71]
/Users/aaron/.rbenv/versions/2.6.4/bin/ruby(0x100ce9035) [0x100ce9035]
/Users/aaron/.rbenv/versions/2.6.4/bin/ruby(0x100ce8a14) [0x100ce8a14]
/Users/aaron/.rbenv/versions/2.6.4/bin/ruby(0x100dfdd86) [0x100dfdd86]
/Users/aaron/.rbenv/versions/2.6.4/bin/ruby(0x100e69a61) [0x100e69a61]
/Users/aaron/.rbenv/versions/2.6.4/bin/ruby(0x100e5032f) [0x100e5032f]
/Users/aaron/.rbenv/versions/2.6.4/bin/ruby(0x100e644c0) [0x100e644c0]
/Users/aaron/.rbenv/versions/2.6.4/bin/ruby(0x100e5d71e) [0x100e5d71e]
/Users/aaron/.rbenv/versions/2.6.4/bin/ruby(0x100c41891) [0x100c41891]
/Users/aaron/.rbenv/versions/2.6.4/bin/ruby(0x100e69a61) [0x100e69a61]
/Users/aaron/.rbenv/versions/2.6.4/bin/ruby(0x100e50087) [0x100e50087]
/Users/aaron/.rbenv/versions/2.6.4/bin/ruby(0x100e644c0) [0x100e644c0]
/Users/aaron/.rbenv/versions/2.6.4/bin/ruby(0x100e58e70) [0x100e58e70]
/Users/aaron/.rbenv/versions/2.6.4/bin/ruby(0x100e5a9ec) [0x100e5a9ec]
/Users/aaron/.rbenv/versions/2.6.4/bin/ruby(0x100e5bd41) [0x100e5bd41]
/Users/aaron/.rbenv/versions/2.6.4/bin/ruby(0x100cc2924) [0x100cc2924]
/Users/aaron/.rbenv/versions/2.6.4/bin/ruby(0x100ccbbb4) [0x100ccbbb4]
/Users/aaron/.rbenv/versions/2.6.4/bin/ruby(0x100ccc359) [0x100ccc359]
/Users/aaron/.rbenv/versions/2.6.4/bin/ruby(0x100ccc993) [0x100ccc993]
/Users/aaron/.rbenv/versions/2.6.4/bin/ruby(0x100cccc40) [0x100cccc40]
/Users/aaron/.rbenv/versions/2.6.4/bin/ruby(0x100c3735d) [0x100c3735d]

-- Other runtime information -----------------------------------------------

* Loaded script: thing.rb

* Loaded features:

    0 enumerator.so
    1 thread.rb
    2 rational.so
    3 complex.so
    4 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/x86_64-darwin18/enc/encdb.bundle
    5 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/x86_64-darwin18/enc/trans/transdb.bundle
    6 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/x86_64-darwin18/rbconfig.rb
    7 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/rubygems/compatibility.rb
    8 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/rubygems/defaults.rb
    9 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/rubygems/deprecate.rb
   10 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/rubygems/errors.rb
   11 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/rubygems/version.rb
   12 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/rubygems/requirement.rb
   13 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/rubygems/platform.rb
   14 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/rubygems/basic_specification.rb
   15 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/rubygems/stub_specification.rb
   16 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/delegate.rb
   17 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/uri/rfc2396_parser.rb
   18 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/uri/rfc3986_parser.rb
   19 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/uri/common.rb
   20 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/uri/generic.rb
   21 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/uri/file.rb
   22 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/uri/ftp.rb
   23 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/uri/http.rb
   24 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/uri/https.rb
   25 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/uri/ldap.rb
   26 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/uri/ldaps.rb
   27 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/uri/mailto.rb
   28 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/uri.rb
   29 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/rubygems/specification_policy.rb
   30 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/rubygems/util/list.rb
   31 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/x86_64-darwin18/stringio.bundle
   32 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/rubygems/specification.rb
   33 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/rubygems/exceptions.rb
   34 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/rubygems/util.rb
   35 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/rubygems/bundler_version_finder.rb
   36 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/rubygems/dependency.rb
   37 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/rubygems/core_ext/kernel_gem.rb
   38 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/monitor.rb
   39 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb
   40 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/rubygems/core_ext/kernel_warn.rb
   41 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/rubygems.rb
   42 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/rubygems/path_support.rb
   43 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/version.rb
   44 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/core_ext/name_error.rb
   45 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/levenshtein.rb
   46 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/jaro_winkler.rb
   47 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/spell_checker.rb
   48 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/spell_checkers/name_error_checkers/class_name_checker.rb
   49 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb
   50 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/spell_checkers/name_error_checkers.rb
   51 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/spell_checkers/method_name_checker.rb
   52 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/spell_checkers/key_error_checker.rb
   53 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/spell_checkers/null_checker.rb
   54 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/formatters/plain_formatter.rb
   55 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean.rb
   56 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/nokogiri.bundle
   57 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/version.rb
   58 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/syntax_error.rb
   59 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/pp/node.rb
   60 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/pp/character_data.rb
   61 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/pp.rb
   62 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/parse_options.rb
   63 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/sax/document.rb
   64 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/sax/parser_context.rb
   65 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/sax/parser.rb
   66 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/sax/push_parser.rb
   67 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/sax.rb
   68 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/searchable.rb
   69 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/node/save_options.rb
   70 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/node.rb
   71 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/attribute_decl.rb
   72 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/element_decl.rb
   73 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/element_content.rb
   74 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/character_data.rb
   75 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/namespace.rb
   76 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/attr.rb
   77 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/dtd.rb
   78 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/cdata.rb
   79 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/text.rb
   80 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/document.rb
   81 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/document_fragment.rb
   82 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/processing_instruction.rb
   83 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/node_set.rb
   84 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/syntax_error.rb
   85 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/xpath/syntax_error.rb
   86 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/xpath.rb
   87 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/xpath_context.rb
   88 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/builder.rb
   89 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/reader.rb
   90 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/notation.rb
   91 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/entity_decl.rb
   92 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/entity_reference.rb
   93 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/schema.rb
   94 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml/relax_ng.rb
   95 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xml.rb
   96 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xslt/stylesheet.rb
   97 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/xslt.rb
   98 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/html/entity_lookup.rb
   99 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/html/document.rb
  100 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/html/document_fragment.rb
  101 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/html/sax/parser_context.rb
  102 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/html/sax/parser.rb
  103 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/html/sax/push_parser.rb
  104 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/html/element_description.rb
  105 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/html/element_description_defaults.rb
  106 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/html.rb
  107 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/decorators/slop.rb
  108 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/css/node.rb
  109 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/css/xpath_visitor.rb
  110 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/x86_64-darwin18/racc/cparse.bundle
  111 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/racc/parser.rb
  112 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/css/parser_extras.rb
  113 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/css/parser.rb
  114 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/2.6.0/x86_64-darwin18/strscan.bundle
  115 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/css/tokenizer.rb
  116 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/css/syntax_error.rb
  117 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/css.rb
  118 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri/html/builder.rb
  119 /Users/aaron/.rbenv/versions/2.6.4/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.7/lib/nokogiri.rb

[NOTE]
You may have encountered a bug in the Ruby interpreter or extension libraries.
Bug reports are welcome.
For details: https://www.ruby-lang.org/bugreport.html

[IMPORTANT]
Don't forget to include the Crash Report log file under
DiagnosticReports directory in bug reports.

fish: 'env MallocScribble=1 ruby thing…' terminated by signal SIGABRT (Abort)
@tenderlove
Copy link
Member

@tenderlove tenderlove commented Feb 26, 2020

Sorry for the wait. I submitted an issue upstream because I couldn't figure out a good way to fix this:

https://gitlab.gnome.org/GNOME/libxml2/issues/148

I'll submit a PR that has my workaround patch.

tenderlove added a commit that referenced this issue Feb 26, 2020
This commit works around a bug in libxml2 where parsing schemas can
result in dangling pointers which can lead to a segv.

Upstream bug is here:  https://gitlab.gnome.org/GNOME/libxml2/issues/148

Fixes #1985
@flavorjones
Copy link
Member

@flavorjones flavorjones commented Feb 26, 2020

@aleksandrs-ledovskis I'm curious if the PR @tenderlove wrote will help your case; it raises an exception if any blank nodes have been wrapped by a Ruby object. Your original description indicated this was a production problem for you, from which I infer you're creating Ruby objects for the DOM in your schema. Before I merge it, just wanted to gut-check whether it's helpful or not.

@flavorjones flavorjones added this to the v1.10.x patch releases milestone Feb 26, 2020
@tenderlove
Copy link
Member

@tenderlove tenderlove commented Feb 26, 2020

@flavorjones probably getting an exception is better than a segv for a case we know we can't handle. 😬

@aleksandrs-ledovskis
Copy link
Author

@aleksandrs-ledovskis aleksandrs-ledovskis commented Mar 1, 2020

@tenderlove Thanks for the patch and upstream report! 🍪

I can confirm that locally built 74abb4f is correctly failing with *** ArgumentError Exception: Creating a schema from a document that has blank nodes exposed to Ruby is dangerous when run in both aforementioned test situations and in Ruby application.


@flavorjones

I infer you're creating Ruby objects for the DOM in your schema

In fact not. The failing app in question deals with incoming SOAP requests and validates them against defined XML schema (read from file). A quirk in current app's implementation is that Nokogiri::XML document is not passed carbon-copied to Nokogiri::XML::Schema.from_document, but is conditionally preprocessed in a way that exposes blank Libxml2 text nodes to Ruby VM.

Approximately like this:

xsd_doc = File.open(path_to_xsd_file) { |file| Nokogiri::XML(file) }

if xsd_doc.xpath("//xs:import[@namespace='http://schemas.xmlsoap.org/soap/envelope/']").empty?
  xsd_doc.xpath("//xs:schema")[0].children.first.add_previous_sibling("import tag with envelope schemaLocation")
end

xsd_schema = Nokogiri::XML::Schema.from_document(xsd_doc)

Obviously, the offending call sequence is xpath("//xs:schema")[0].children.first, which can be alternatively (safely) represented by at_xpath("//xs:schema/*").

The latter variant is safe/doesn't cause an exception with 74abb4f patch - while Nokogiri::XML::Node#children returns all nodes including blanks, the Nokogiri::XML::Searchable#xpath uses Libxml2 implementation of XPath lookup which ignores all blanks.

Before I merge it, just wanted to gut-check whether it's helpful or not.

Yes, the @tenderlove's patch is helpful. If in some other code path/project the children.first approach will be used unbeknownst of Libxml2 pitfalls concerning schema preparation, at least the "fail early" exception should promptly notify about the sneaky failure mode.

@flavorjones
Copy link
Member

@flavorjones flavorjones commented Mar 1, 2020

@aleksandrs-ledovskis Great, thanks for the explanation - I didn't want to merge this if you didn't have a path forward to do what you need to do.

@flavorjones
Copy link
Member

@flavorjones flavorjones commented Mar 1, 2020

I've merged the PR. I'll be cutting a bugfix release today - likely v1.10.9 - with this fix.

flavorjones added a commit that referenced this issue Mar 1, 2020
flavorjones added a commit that referenced this issue Mar 1, 2020
This commit works around a bug in libxml2 where parsing schemas can
result in dangling pointers which can lead to a segv.

Upstream bug is here:  https://gitlab.gnome.org/GNOME/libxml2/issues/148

Fixes #1985
flavorjones added a commit that referenced this issue Mar 1, 2020
@flavorjones
Copy link
Member

@flavorjones flavorjones commented Mar 2, 2020

v1.10.9 was released yesterday

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

4 participants