Skip to content

Commit d1b09ab

Browse files
sbstnkwismill
authored andcommitted
registry: Restore default libxml2 error handler after parsing
Leaving the custom error handler could have resulted in a crash after the context has been freed. Closes: #529
1 parent b888834 commit d1b09ab

File tree

4 files changed

+47
-4
lines changed

4 files changed

+47
-4
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
registry: Fixed `libxml2` global error handler not reset after parsing, which
2+
could trigger a crash if the corresponding `rxkb_context` has been freed.
3+
4+
Contributed by Sebastian Keller.

meson.build

+1-1
Original file line numberDiff line numberDiff line change
@@ -879,7 +879,7 @@ if get_option('enable-xkbregistry')
879879
'registry',
880880
executable('test-registry', 'test/registry.c',
881881
include_directories: include_directories('src'),
882-
dependencies: [dep_libxkbregistry, test_dep]),
882+
dependencies: [dep_libxkbregistry, dep_libxml, test_dep]),
883883
env: test_env,
884884
)
885885
endif

src/registry.c

+15-3
Original file line numberDiff line numberDiff line change
@@ -1250,20 +1250,32 @@ parse(struct rxkb_context *ctx, const char *path,
12501250

12511251
doc = xmlParseFile(path);
12521252
if (!doc)
1253-
return false;
1253+
goto parse_error;
12541254

12551255
if (!validate(ctx, doc)) {
12561256
log_err(ctx, XKB_LOG_MESSAGE_NO_ID,
12571257
"XML error: failed to validate document at %s\n", path);
1258-
goto error;
1258+
goto validate_error;
12591259
}
12601260

12611261
root = xmlDocGetRootElement(doc);
12621262
parse_rules_xml(ctx, root, popularity);
12631263

12641264
success = true;
1265-
error:
1265+
validate_error:
12661266
xmlFreeDoc(doc);
1267+
parse_error:
1268+
/*
1269+
* Reset the default libxml2 error handler to default, because this handler
1270+
* is global and may be used on an invalid rxkb_context, e.g. *after* the
1271+
* following sequence:
1272+
*
1273+
* rxkb_context_new();
1274+
* rxkb_context_parse();
1275+
* rxkb_context_unref();
1276+
*/
1277+
/* TODO: use the new API xmlCtxtSetErrorHandler */
1278+
xmlSetGenericErrorFunc(NULL, NULL);
12671279

12681280
return success;
12691281
}

test/registry.c

+27
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
#include <stdio.h>
3232
#include <sys/stat.h>
3333
#include <sys/types.h>
34+
#include <libxml/parser.h>
35+
#include <libxml/tree.h>
3436

3537
#include "xkbcommon/xkbregistry.h"
3638

@@ -1065,11 +1067,36 @@ test_invalid_include(void)
10651067
rxkb_context_unref(ctx);
10661068
}
10671069

1070+
/* Check that libxml2 error handler is reset after parsing */
1071+
static void
1072+
test_xml_error_handler(void)
1073+
{
1074+
struct test_model system_models[] = { {NULL} };
1075+
struct test_layout system_layouts[] = { {NULL} };
1076+
struct test_option_group system_groups[] = { { NULL } };
1077+
struct rxkb_context *ctx;
1078+
1079+
ctx = test_setup_context(system_models, NULL,
1080+
system_layouts, NULL,
1081+
system_groups, NULL);
1082+
assert(ctx);
1083+
rxkb_context_unref(ctx);
1084+
1085+
const char invalid_xml[] = "<test";
1086+
/* This should trigger a segfault if error handler is not reset, because
1087+
* else it would use our error handler with an invalid (freed) context. */
1088+
xmlDocPtr doc = xmlParseMemory (invalid_xml, strlen(invalid_xml));
1089+
assert(!doc);
1090+
xmlFreeDoc (doc);
1091+
xmlCleanupParser();
1092+
}
1093+
10681094
int
10691095
main(void)
10701096
{
10711097
test_init();
10721098

1099+
test_xml_error_handler();
10731100
test_no_include_paths();
10741101
test_invalid_include();
10751102
test_load_basic();

0 commit comments

Comments
 (0)