Skip to content

Commit eba2097

Browse files
TreyEdginev
authored andcommitted
Protect xmlSchemaInitTypes from thread-unsafe behaviour.
`xmlSchemaParse` calls `xmlSchemaInitTypes`. `xmlSchemaInitTypes` is a lazy function which is only intended to be called once for optimization purposes - but libxml2 doesn't do this in a thread-safe manner. We wrap the call in a OnceLock so that it only ever needs to be invoked once - and will do it in a thread-safe way.
1 parent dc2c7c3 commit eba2097

4 files changed

Lines changed: 28 additions & 5 deletions

File tree

src/default_bindings.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10794,6 +10794,9 @@ unsafe extern "C" {
1079410794
unsafe extern "C" {
1079510795
pub fn xmlSchemaIsValid(ctxt: xmlSchemaValidCtxtPtr) -> ::std::os::raw::c_int;
1079610796
}
10797+
unsafe extern "C" {
10798+
pub fn xmlSchemaInitTypes();
10799+
}
1079710800
unsafe extern "C" {
1079810801
pub fn xmlSchemaParse(ctxt: xmlSchemaParserCtxtPtr) -> xmlSchemaPtr;
1079910802
}

src/schemas/schema.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,34 @@
11
//!
22
//! Wrapping of the Schema (xmlSchema)
33
//!
4+
use std::sync::OnceLock;
5+
46
use super::SchemaParserContext;
57

68
use crate::bindings;
79

810
use crate::error::StructuredError;
911

12+
static SCHEMA_TYPES_LOCK: OnceLock<bool> = OnceLock::new();
13+
1014
/// Wrapper on xmlSchema
1115
pub struct Schema(*mut bindings::_xmlSchema);
1216

1317
impl Schema {
1418
/// Create schema by having a SchemaParserContext do the actual parsing of the schema it was provided
1519
pub fn from_parser(parser: &mut SchemaParserContext) -> Result<Self, Vec<StructuredError>> {
20+
21+
// `xmlSchemaParse` calls `xmlSchemaInitTypes`.
22+
// `xmlSchemaInitTypes` is a lazy function which is only intended to be
23+
// called once for optimization purposes - but libxml2 doesn't do this
24+
// in a thread-safe manner. We wrap the call in a OnceLock so that it
25+
// only ever needs to be invoked once - and will do it in a thread-safe
26+
// way.
27+
let _ = SCHEMA_TYPES_LOCK.get_or_init(|| {
28+
unsafe { bindings::xmlSchemaInitTypes() };
29+
true
30+
});
31+
1632
let raw = unsafe { bindings::xmlSchemaParse(parser.as_ptr()) };
1733

1834
if raw.is_null() {

src/wrapper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
#include <libxml/xpathInternals.h>
44
#include <libxml/xmlsave.h>
55
#include <libxml/HTMLparser.h>
6+
#include <libxml/xmlschemastypes.h>
67
#include <libxml/xmlschemas.h>

tests/schema_tests.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,7 @@ static INVALID_STOCK_XML: &str = r#"<?xml version="1.0"?>
8383
// while it still reliably succeeds single-threaded, new implementation is needed to use
8484
// these in a parallel setting.
8585
#[test]
86-
fn schema_all_tests() {
87-
// fn schema_from_string() {
86+
fn schema_from_string() {
8887
let xml = Parser::default()
8988
.parse_string(VALID_NOTE_XML)
9089
.expect("Expected to be able to parse XML Document from string");
@@ -111,8 +110,10 @@ fn schema_all_tests() {
111110
panic!("Invalid XML accoding to XSD schema");
112111
}
113112
}
114-
115-
// fn schema_from_string_generates_errors() {
113+
}
114+
115+
#[test]
116+
fn schema_from_string_generates_errors() {
116117
let xml = Parser::default()
117118
.parse_string(INVALID_NOTE_XML)
118119
.expect("Expected to be able to parse XML Document from string");
@@ -138,8 +139,10 @@ fn schema_all_tests() {
138139
}
139140
}
140141
}
142+
}
141143

142-
// fn schema_from_string_reports_unique_errors() {
144+
#[test]
145+
fn schema_from_string_reports_unique_errors() {
143146
let xml = Parser::default()
144147
.parse_string(INVALID_STOCK_XML)
145148
.expect("Expected to be able to parse XML Document from string");

0 commit comments

Comments
 (0)