Skip to content

Commit

Permalink
registry: Restore default libxml2 error handler after parsing
Browse files Browse the repository at this point in the history
Leaving the custom error handler could have resulted in a crash after
the context has been freed.

Closes: #529
  • Loading branch information
sbstnk authored and wismill committed Oct 13, 2024
1 parent b888834 commit a47961b
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 4 deletions.
4 changes: 4 additions & 0 deletions changes/api/529.fix-xml-error-handler.bugfix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
registry: Fixed `libxml2` global error handler not reset after parsing, which
could trigger a crash if the corresponding `rxkb_context` has been freed.

Contributed by Sebastian Keller.
2 changes: 1 addition & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -879,7 +879,7 @@ if get_option('enable-xkbregistry')
'registry',
executable('test-registry', 'test/registry.c',
include_directories: include_directories('src'),
dependencies: [dep_libxkbregistry, test_dep]),
dependencies: [dep_libxkbregistry, dep_libxml, test_dep]),
env: test_env,
)
endif
Expand Down
18 changes: 15 additions & 3 deletions src/registry.c
Original file line number Diff line number Diff line change
Expand Up @@ -1250,20 +1250,32 @@ parse(struct rxkb_context *ctx, const char *path,

doc = xmlParseFile(path);
if (!doc)
return false;
goto parse_error;

if (!validate(ctx, doc)) {
log_err(ctx, XKB_LOG_MESSAGE_NO_ID,
"XML error: failed to validate document at %s\n", path);
goto error;
goto validate_error;
}

root = xmlDocGetRootElement(doc);
parse_rules_xml(ctx, root, popularity);

success = true;
error:
validate_error:
xmlFreeDoc(doc);
parse_error:
/*
* Reset the default libxml2 error handler to default, because this handler
* is global and may be used on an invalid rxkb_context, e.g. *after* the
* following sequence:
*
* rxkb_context_new();
* rxkb_context_parse();
* rxkb_context_unref();
*/
/* TODO: use the new API xmlCtxtSetErrorHandler */
xmlSetGenericErrorFunc(NULL, NULL);

return success;
}
27 changes: 27 additions & 0 deletions test/registry.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <libxml/parser.h>
#include <libxml/tree.h>

#include "xkbcommon/xkbregistry.h"

Expand Down Expand Up @@ -1065,11 +1067,36 @@ test_invalid_include(void)
rxkb_context_unref(ctx);
}

/* Check that libxml2 error handler is reset after parsing */
static void
test_xml_error_handler(void)
{
struct test_model system_models[] = { {NULL} };
struct test_layout system_layouts[] = { {NULL} };
struct test_option_group system_groups[] = { { NULL } };
struct rxkb_context *ctx;

ctx = test_setup_context(system_models, NULL,
system_layouts, NULL,
system_groups, NULL);
assert(ctx);
rxkb_context_unref(ctx);

const char invalid_xml[] = "<test";
/* This should trigger a segfault if error handler is not reset, because
* else it would use our error handler with an invalid (freed) context. */
xmlDocPtr doc = xmlParseMemory (invalid_xml, strlen(invalid_xml));
assert(!doc);
xmlFreeDoc (doc);
xmlCleanupParser();
}

int
main(void)
{
test_init();

test_xml_error_handler();
test_no_include_paths();
test_invalid_include();
test_load_basic();
Expand Down

0 comments on commit a47961b

Please sign in to comment.