Skip to content

Commit 1525fe0

Browse files
authored
Merge pull request #299 from jonathangreen/feature/version-check
libxml2 version check
2 parents abccdbe + c7e3208 commit 1525fe0

File tree

5 files changed

+116
-10
lines changed

5 files changed

+116
-10
lines changed

src/lxml.c

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include "common.h"
1111
#include "lxml.h"
12+
#include "exception.h"
1213

1314
#include <etree_defs.h>
1415
#include <etree_api.h>
@@ -17,8 +18,85 @@
1718
#include <libxml/parser.h>
1819
#include <libxml/dict.h>
1920

21+
#define XMLSEC_EXTRACT_VERSION(x, y) ((x / (y)) % 100)
22+
23+
#define XMLSEC_EXTRACT_MAJOR(x) XMLSEC_EXTRACT_VERSION(x, 100 * 100)
24+
#define XMLSEC_EXTRACT_MINOR(x) XMLSEC_EXTRACT_VERSION(x, 100)
25+
#define XMLSEC_EXTRACT_PATCH(x) XMLSEC_EXTRACT_VERSION(x, 1)
26+
27+
static long PyXmlSec_GetLibXmlVersionLong() {
28+
return PyOS_strtol(xmlParserVersion, NULL, 10);
29+
}
30+
long PyXmlSec_GetLibXmlVersionMajor() {
31+
return XMLSEC_EXTRACT_MAJOR(PyXmlSec_GetLibXmlVersionLong());
32+
}
33+
long PyXmlSec_GetLibXmlVersionMinor() {
34+
return XMLSEC_EXTRACT_MINOR(PyXmlSec_GetLibXmlVersionLong());
35+
}
36+
long PyXmlSec_GetLibXmlVersionPatch() {
37+
return XMLSEC_EXTRACT_PATCH(PyXmlSec_GetLibXmlVersionLong());
38+
}
39+
40+
long PyXmlSec_GetLibXmlCompiledVersionMajor() {
41+
return XMLSEC_EXTRACT_MAJOR(LIBXML_VERSION);
42+
}
43+
long PyXmlSec_GetLibXmlCompiledVersionMinor() {
44+
return XMLSEC_EXTRACT_MINOR(LIBXML_VERSION);
45+
}
46+
long PyXmlSec_GetLibXmlCompiledVersionPatch() {
47+
return XMLSEC_EXTRACT_PATCH(LIBXML_VERSION);
48+
}
49+
50+
static int PyXmlSec_CheckLxmlLibraryVersion(void) {
51+
// Make sure that the version of libxml2 lxml is using is the same as the one we are using. Because
52+
// we pass trees between the two libraries, we need to make sure that they are using the same version
53+
// of libxml2, or we could run into difficult to debug segfaults.
54+
// See: https://github.com/xmlsec/python-xmlsec/issues/283
55+
56+
PyObject* lxml = NULL;
57+
PyObject* version = NULL;
58+
59+
// Default to failure
60+
int result = -1;
61+
62+
lxml = PyImport_ImportModule("lxml.etree");
63+
if (lxml == NULL) {
64+
goto FINALIZE;
65+
}
66+
version = PyObject_GetAttrString(lxml, "LIBXML_VERSION");
67+
if (version == NULL) {
68+
goto FINALIZE;
69+
}
70+
if (!PyTuple_Check(version) || PyTuple_Size(version) != 3) {
71+
goto FINALIZE;
72+
}
73+
74+
PyObject* major = PyTuple_GetItem(version, 0);
75+
PyObject* minor = PyTuple_GetItem(version, 1);
76+
77+
if (!PyLong_Check(major) || !PyLong_Check(minor)) {
78+
goto FINALIZE;
79+
}
80+
81+
if (PyLong_AsLong(major) != PyXmlSec_GetLibXmlVersionMajor() || PyLong_AsLong(minor) != PyXmlSec_GetLibXmlVersionMinor()) {
82+
goto FINALIZE;
83+
}
84+
85+
result = 0;
86+
87+
FINALIZE:
88+
// Cleanup our references, and return the result
89+
Py_XDECREF(lxml);
90+
Py_XDECREF(version);
91+
return result;
92+
}
2093

2194
int PyXmlSec_InitLxmlModule(void) {
95+
if (PyXmlSec_CheckLxmlLibraryVersion() < 0) {
96+
PyXmlSec_SetLastError("lxml & xmlsec libxml2 library version mismatch");
97+
return -1;
98+
}
99+
22100
return import_lxml__etree();
23101
}
24102

src/lxml.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,13 @@ PyXmlSec_LxmlElementPtr PyXmlSec_elementFactory(PyXmlSec_LxmlDocumentPtr doc, xm
2929
// converts o to PyObject, None object is not allowed, does not increment ref_counts
3030
int PyXmlSec_LxmlElementConverter(PyObject* o, PyXmlSec_LxmlElementPtr* p);
3131

32+
// get version numbers for libxml2 both compiled and loaded
33+
long PyXmlSec_GetLibXmlVersionMajor();
34+
long PyXmlSec_GetLibXmlVersionMinor();
35+
long PyXmlSec_GetLibXmlVersionPatch();
36+
37+
long PyXmlSec_GetLibXmlCompiledVersionMajor();
38+
long PyXmlSec_GetLibXmlCompiledVersionMinor();
39+
long PyXmlSec_GetLibXmlCompiledVersionPatch();
40+
3241
#endif // __PYXMLSEC_LXML_H__

src/main.c

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "common.h"
1111
#include "platform.h"
1212
#include "exception.h"
13+
#include "lxml.h"
1314

1415
#include <xmlsec/xmlsec.h>
1516
#include <xmlsec/crypto.h>
@@ -127,10 +128,27 @@ static PyObject* PyXmlSec_GetLibXmlSecVersion() {
127128
}
128129

129130
static char PyXmlSec_GetLibXmlVersion__doc__[] = \
130-
"get_libxml_version() -> tuple\n"
131-
"Returns Version tuple of wrapped libxml library.";
131+
"get_libxml_version() -> tuple[int, int, int]\n"
132+
"Returns version tuple of libxml2 library xmlsec is using.";
132133
static PyObject* PyXmlSec_GetLibXmlVersion() {
133-
return Py_BuildValue("(iii)", XMLSEC_LIBXML_VERSION_MAJOR, XMLSEC_LIBXML_VERSION_MINOR, XMLSEC_LIBXML_VERSION_PATCH);
134+
return Py_BuildValue(
135+
"(iii)",
136+
PyXmlSec_GetLibXmlVersionMajor(),
137+
PyXmlSec_GetLibXmlVersionMinor(),
138+
PyXmlSec_GetLibXmlVersionPatch()
139+
);
140+
}
141+
142+
static char PyXmlSec_GetLibXmlCompiledVersion__doc__[] = \
143+
"get_libxml_compiled_version() -> tuple[int, int, int]\n"
144+
"Returns version tuple of libxml2 library xmlsec was compiled with.";
145+
static PyObject* PyXmlSec_GetLibXmlCompiledVersion() {
146+
return Py_BuildValue(
147+
"(iii)",
148+
PyXmlSec_GetLibXmlCompiledVersionMajor(),
149+
PyXmlSec_GetLibXmlCompiledVersionMinor(),
150+
PyXmlSec_GetLibXmlCompiledVersionPatch()
151+
);
134152
}
135153

136154
static char PyXmlSec_PyEnableDebugOutput__doc__[] = \
@@ -412,6 +430,12 @@ static PyMethodDef PyXmlSec_MainMethods[] = {
412430
METH_NOARGS,
413431
PyXmlSec_GetLibXmlVersion__doc__
414432
},
433+
{
434+
"get_libxml_compiled_version",
435+
(PyCFunction)PyXmlSec_GetLibXmlCompiledVersion,
436+
METH_NOARGS,
437+
PyXmlSec_GetLibXmlCompiledVersion__doc__
438+
},
415439
{
416440
"enable_debug_trace",
417441
(PyCFunction)PyXmlSec_PyEnableDebugOutput,

src/platform.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,13 @@
1212

1313
#define PY_SSIZE_T_CLEAN 1
1414

15-
#include <libxml/xmlversion.h>
1615
#include <xmlsec/version.h>
1716
#include <Python.h>
1817

1918
#ifdef MS_WIN32
2019
#include <windows.h>
2120
#endif /* MS_WIN32 */
2221

23-
#define XMLSEC_EXTRACT_VERSION(x, y) ((x / (y)) % 100)
24-
25-
#define XMLSEC_LIBXML_VERSION_MAJOR XMLSEC_EXTRACT_VERSION(LIBXML_VERSION, 100 * 100)
26-
#define XMLSEC_LIBXML_VERSION_MINOR XMLSEC_EXTRACT_VERSION(LIBXML_VERSION, 100)
27-
#define XMLSEC_LIBXML_VERSION_PATCH XMLSEC_EXTRACT_VERSION(LIBXML_VERSION, 1)
28-
2922
#define XMLSEC_VERSION_HEX ((XMLSEC_VERSION_MAJOR << 16) | (XMLSEC_VERSION_MINOR << 8) | (XMLSEC_VERSION_SUBMINOR))
3023

3124
// XKMS support was removed in version 1.2.21

src/xmlsec/__init__.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ from xmlsec.constants import __Transform as Transform
1313
_E = TypeVar('_E', bound=_Element)
1414

1515
def enable_debug_trace(enabled: bool = ...) -> None: ...
16+
def get_libxml_version() -> tuple[int, int, int]: ...
17+
def get_libxml_compiled_version() -> tuple[int, int, int]: ...
1618
def init() -> None: ...
1719
def shutdown() -> None: ...
1820
def cleanup_callbacks() -> None: ...

0 commit comments

Comments
 (0)