diff --git a/config/patches/ruby/fix-ruby-xfree-for-libxml2-2.7.patch b/config/patches/ruby/fix-ruby-xfree-for-libxml2-2.7.patch
new file mode 100644
index 0000000000000000000000000000000000000000..ac847a87405d4026e780009585e94b8a163256cd_Y29uZmlnL3BhdGNoZXMvcnVieS9maXgtcnVieS14ZnJlZS1mb3ItbGlieG1sMi0yLjcucGF0Y2g=
--- /dev/null
+++ b/config/patches/ruby/fix-ruby-xfree-for-libxml2-2.7.patch
@@ -0,0 +1,23 @@
+diff --git a/gc.c b/gc.c
+index 67a709ff79..98785901f4 100644
+--- a/gc.c
++++ b/gc.c
+@@ -10174,8 +10174,16 @@ ruby_xrealloc2_body(void *ptr, size_t n, size_t size)
+ void
+ ruby_sized_xfree(void *x, size_t size)
+ {
+-    if (x) {
+-	objspace_xfree(&rb_objspace, x, size);
++    if (LIKELY(x)) {
++        /* It's possible for a C extension's pthread destructor function set by pthread_key_create
++         * to be called after ruby_vm_destruct and attempt to free memory. Fall back to mimfree in
++         * that case. */
++        if (LIKELY(GET_VM())) {
++            objspace_xfree(&rb_objspace, x, size);
++        }
++        else {
++            ruby_mimfree(x);
++        }
+     }
+ }
+ 
diff --git a/config/patches/ruby/fix-ruby-xfree-for-libxml2-3.0.patch b/config/patches/ruby/fix-ruby-xfree-for-libxml2-3.0.patch
new file mode 100644
index 0000000000000000000000000000000000000000..ac847a87405d4026e780009585e94b8a163256cd_Y29uZmlnL3BhdGNoZXMvcnVieS9maXgtcnVieS14ZnJlZS1mb3ItbGlieG1sMi0zLjAucGF0Y2g=
--- /dev/null
+++ b/config/patches/ruby/fix-ruby-xfree-for-libxml2-3.0.patch
@@ -0,0 +1,23 @@
+diff --git a/gc.c b/gc.c
+index 5d0c342206..2bfff21004 100644
+--- a/gc.c
++++ b/gc.c
+@@ -10905,8 +10905,16 @@ ruby_xrealloc2_body(void *ptr, size_t n, size_t size)
+ void
+ ruby_sized_xfree(void *x, size_t size)
+ {
+-    if (x) {
+-	objspace_xfree(&rb_objspace, x, size);
++    if (LIKELY(x)) {
++        /* It's possible for a C extension's pthread destructor function set by pthread_key_create
++         * to be called after ruby_vm_destruct and attempt to free memory. Fall back to mimfree in
++         * that case. */
++        if (LIKELY(GET_VM())) {
++            objspace_xfree(&rb_objspace, x, size);
++        }
++        else {
++            ruby_mimfree(x);
++        }
+     }
+ }
+ 
diff --git a/config/patches/ruby/fix-ruby-xfree-for-libxml2-3.1.patch b/config/patches/ruby/fix-ruby-xfree-for-libxml2-3.1.patch
new file mode 100644
index 0000000000000000000000000000000000000000..ac847a87405d4026e780009585e94b8a163256cd_Y29uZmlnL3BhdGNoZXMvcnVieS9maXgtcnVieS14ZnJlZS1mb3ItbGlieG1sMi0zLjEucGF0Y2g=
--- /dev/null
+++ b/config/patches/ruby/fix-ruby-xfree-for-libxml2-3.1.patch
@@ -0,0 +1,23 @@
+diff --git a/gc.c b/gc.c
+index 030a4627bd..1c96f6401f 100644
+--- a/gc.c
++++ b/gc.c
+@@ -11763,8 +11763,16 @@ ruby_xrealloc2_body(void *ptr, size_t n, size_t size)
+ void
+ ruby_sized_xfree(void *x, size_t size)
+ {
+-    if (x) {
+-	objspace_xfree(&rb_objspace, x, size);
++    if (LIKELY(x)) {
++        /* It's possible for a C extension's pthread destructor function set by pthread_key_create
++         * to be called after ruby_vm_destruct and attempt to free memory. Fall back to mimfree in
++         * that case. */
++        if (LIKELY(GET_VM())) {
++            objspace_xfree(&rb_objspace, x, size);
++        }
++        else {
++            ruby_mimfree(x);
++        }
+     }
+ }
+ 
diff --git a/config/patches/ruby/fix-ruby-xfree-for-libxml2-3.2.patch b/config/patches/ruby/fix-ruby-xfree-for-libxml2-3.2.patch
new file mode 100644
index 0000000000000000000000000000000000000000..ac847a87405d4026e780009585e94b8a163256cd_Y29uZmlnL3BhdGNoZXMvcnVieS9maXgtcnVieS14ZnJlZS1mb3ItbGlieG1sMi0zLjIucGF0Y2g=
--- /dev/null
+++ b/config/patches/ruby/fix-ruby-xfree-for-libxml2-3.2.patch
@@ -0,0 +1,23 @@
+diff --git a/gc.c b/gc.c
+index 54400e4f6b..96d43ac8ac 100644
+--- a/gc.c
++++ b/gc.c
+@@ -12614,8 +12614,16 @@ ruby_xrealloc2_body(void *ptr, size_t n, size_t size)
+ void
+ ruby_sized_xfree(void *x, size_t size)
+ {
+-    if (x) {
+-        objspace_xfree(&rb_objspace, x, size);
++    if (LIKELY(x)) {
++        /* It's possible for a C extension's pthread destructor function set by pthread_key_create
++         * to be called after ruby_vm_destruct and attempt to free memory. Fall back to mimfree in
++         * that case. */
++        if (LIKELY(GET_VM())) {
++            objspace_xfree(&rb_objspace, x, size);
++        }
++        else {
++            ruby_mimfree(x);
++        }
+     }
+ }
+ 
diff --git a/config/software/ruby.rb b/config/software/ruby.rb
index 0d4c30382df61a7ba2690b7f937385248262cd95_Y29uZmlnL3NvZnR3YXJlL3J1YnkucmI=..ac847a87405d4026e780009585e94b8a163256cd_Y29uZmlnL3NvZnR3YXJlL3J1YnkucmI= 100644
--- a/config/software/ruby.rb
+++ b/config/software/ruby.rb
@@ -105,16 +105,16 @@
     # be fixed.
   end
 
-  # Enable custom patch created by ayufan that allows to count memory allocations
-  # per-thread. This is asked to be upstreamed as part of https://github.com/ruby/ruby/pull/3978
-  if version.start_with?('2.7')
-    patch source: 'thread-memory-allocations-2.7.patch', plevel: 1, env: env
-  elsif version.start_with?('3.0')
-    patch source: 'thread-memory-allocations-3.0.patch', plevel: 1, env: env
-  elsif version.start_with?('3.1')
-    patch source: 'thread-memory-allocations-3.1.patch', plevel: 1, env: env
-  elsif version.start_with?('3.2')
-    patch source: 'thread-memory-allocations-3.2.patch', plevel: 1, env: env
+  # Two patches:
+  # 1. Enable custom patch created by ayufan that allows to count memory allocations
+  #    per-thread. This is asked to be upstreamed as part of https://github.com/ruby/ruby/pull/3978
+  # 2. Backport Ruby upstream patch to fix seg faults in libxml2/Nokogiri: https://bugs.ruby-lang.org/issues/19580
+  #    This has been merged for Ruby 3.3 but not yet backported: https://github.com/ruby/ruby/pull/7663
+  patches = %w[thread-memory-allocations fix-ruby-xfree-for-libxml2]
+  ruby_version = Gem::Version.new(version).canonical_segments[0..1].join('.')
+
+  patches.each do |patch_name|
+    patch source: "#{patch_name}-#{ruby_version}.patch", plevel: 1, env: env
   end
 
   # copy_file_range() has been disabled on recent RedHat kernels: