]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
util: workaround libxml2 lack of thread safe initialization
authorDaniel P. Berrangé <berrange@redhat.com>
Mon, 23 Jun 2025 10:50:47 +0000 (11:50 +0100)
committerDaniel P. Berrangé <berrange@redhat.com>
Tue, 24 Jun 2025 11:25:10 +0000 (12:25 +0100)
The main XML parser code global initializer historically had a mutex
protecting it, and more recently uses a pthread_once. The RelaxNG
code, however, relies on two other global initializers that are
not thread safe, just relying on setting an integer "initialized"
flag.

Calling the relevant initializers from libvirt in a protected global
initializer will protect libvirt's own concurrent usage, however, it
cannot protect against other libraries loaded in process that might
be using libxml2's schema code. Fortunately:

 * The chances of other loaded non-libvirt code using libxml is
   relatively low
 * The chances of other loaded non-libvirt code using the schema
   validation / catalog functionality inside libxml is even
   lower
 * The chances of both libvirt and the non-libvirt usage having
   their *1st* usage of libxml2 be concurrent is tiny

IOW, in practice, although our solution doesn't fully fix the thread
safety, it is good enough.

libxml2 should none the less still be fixed to make its global
initializers be thread safe without special actions by its API
consumers[1].

Resolves: https://gitlab.com/libvirt/libvirt/-/issues/788
[1] https://gitlab.gnome.org/GNOME/libxml2/-/merge_requests/326
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
src/util/virxml.c

index 9d46e5f32faabfdb875db19d11211793fd662bcf..bb80bb7444d58264e588d2964b98618bda167a74 100644 (file)
@@ -26,6 +26,8 @@
 
 #include <libxml/xmlsave.h>
 #include <libxml/xpathInternals.h>
+#include <libxml/xmlschemastypes.h>
+#include <libxml/catalog.h>
 
 #include "virerror.h"
 #include "virxml.h"
@@ -35,6 +37,7 @@
 #include "virstring.h"
 #include "virutil.h"
 #include "viruuid.h"
+#include "virthread.h"
 #include "configmake.h"
 
 #define VIR_FROM_THIS VIR_FROM_XML
@@ -50,6 +53,28 @@ struct virParserData {
 };
 
 
+static int
+virXMLSchemaOnceInit(void)
+{
+#if LIBXML_VERSION >= 21100
+    if (xmlSchemaInitTypes() < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Unable to initialize libxml2 schema types"));
+        return -1;
+    }
+#else
+    xmlSchemaInitTypes();
+#endif
+    if (xmlRelaxNGInitTypes() < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Unable to initialize libxml2 RelaxNG data"));
+        return -1;
+    }
+    return 0;
+}
+
+VIR_ONCE_GLOBAL_INIT(virXMLSchema);
+
 static xmlXPathContextPtr
 virXMLXPathContextNew(xmlDocPtr xml)
 {
@@ -1603,6 +1628,9 @@ virXMLValidatorInit(const char *schemafile)
 {
     g_autoptr(virXMLValidator) validator = NULL;
 
+    if (virXMLSchemaInitialize() < 0)
+        return NULL;
+
     validator = g_new0(virXMLValidator, 1);
 
     validator->schemafile = g_strdup(schemafile);