/
githubmirror
/
libxmlb
Обзор
Документация
Войти
/
githubmirror
/
libxmlb
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/xb-silo-export.c
296 строк
8 KB
Richard Hughes
Prevent stack overflow from unbounded recursion in export
13 апр 2026, 20:41
13 апр 2026, 20:41
79fdf66
Код
Авторство
О чём код?
/* * Copyright 2018 Richard Hughes <richard@hughsie.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #define G_LOG_DOMAIN "XbSilo" #include "config.h" #include <gio/gio.h> #include "xb-node-private.h" #include "xb-silo-export-private.h" #include "xb-silo-node.h" #include "xb-string-private.h" #define XB_SILO_EXPORT_MAX_DEPTH 100 typedef struct { GString *xml; XbNodeExportFlags flags; guint32 off; guint level; } XbSiloExportHelper; static gboolean xb_silo_export_node(XbSilo *self, XbSiloExportHelper *helper, XbSiloNode *sn, GError **error) { XbSiloNode *sn2; const gchar *element_name; helper->off = xb_silo_get_offset_for_node(self, sn); /* add start of opening tag */ if (helper->flags & XB_NODE_EXPORT_FLAG_FORMAT_INDENT) { for (guint i = 0; i < helper->level; i++) g_string_append(helper->xml, " "); } element_name = xb_silo_from_strtab(self, sn->element_name, error); if (element_name == NULL) return FALSE; g_string_append_printf(helper->xml, "<%s", element_name); /* add any attributes */ for (guint8 i = 0; i < xb_silo_node_get_attr_count(sn); i++) { XbSiloNodeAttr *a = xb_silo_node_get_attr(sn, i); const gchar *name_unsafe; const gchar *value_unsafe; g_autofree gchar *name = NULL; g_autofree gchar *value = NULL; name_unsafe = xb_silo_from_strtab(self, a->attr_name, error); if (name_unsafe == NULL) return FALSE; name = xb_string_xml_escape(name_unsafe); value_unsafe = xb_silo_from_strtab(self, a->attr_value, error); if (value_unsafe == NULL) return FALSE; value = xb_string_xml_escape(value_unsafe); g_string_append_printf(helper->xml, " %s=\"%s\"", name, value); } /* collapse open/close tags together if no text or children */ if (helper->flags & XB_NODE_EXPORT_FLAG_COLLAPSE_EMPTY && xb_silo_node_get_text_idx(sn) == XB_SILO_UNSET && xb_silo_get_child_node(self, sn, NULL) == NULL) { g_string_append(helper->xml, " />"); /* offset by opening tag and single byte sentinel */ helper->off += xb_silo_node_get_size(sn); sn2 = xb_silo_get_node(self, helper->off, error); if (sn2 == NULL) return FALSE; helper->off += xb_silo_node_get_size(sn2); } else { /* finish the opening tag and add any text if it exists */ if (xb_silo_node_get_text_idx(sn) != XB_SILO_UNSET) { const gchar *text_unsafe; g_autofree gchar *text = NULL; text_unsafe = xb_silo_from_strtab(self, xb_silo_node_get_text_idx(sn), error); if (text_unsafe == NULL) return FALSE; text = xb_string_xml_escape(text_unsafe); g_string_append(helper->xml, ">"); g_string_append(helper->xml, text); } else { g_string_append(helper->xml, ">"); if (helper->flags & XB_NODE_EXPORT_FLAG_FORMAT_MULTILINE) g_string_append(helper->xml, "\n"); } helper->off += xb_silo_node_get_size(sn); /* recurse deeper */ while (TRUE) { XbSiloNode *child = xb_silo_get_node(self, helper->off, error); if (child == NULL) return FALSE; if (!xb_silo_node_has_flag(child, XB_SILO_NODE_FLAG_IS_ELEMENT)) break; /* check recursion depth to prevent stack overflow */ if (helper->level >= XB_SILO_EXPORT_MAX_DEPTH) { g_set_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "nesting deeper than %u levels not supported", (guint)XB_SILO_EXPORT_MAX_DEPTH); return FALSE; } helper->level++; if (!xb_silo_export_node(self, helper, child, error)) return FALSE; helper->level--; } /* check for the single byte sentinel */ sn2 = xb_silo_get_node(self, helper->off, error); if (sn2 == NULL) return FALSE; if (xb_silo_node_has_flag(sn2, XB_SILO_NODE_FLAG_IS_ELEMENT)) { g_set_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "no seninel at %" G_GUINT32_FORMAT, helper->off); return FALSE; } helper->off += xb_silo_node_get_size(sn2); /* add closing tag */ if ((helper->flags & XB_NODE_EXPORT_FLAG_FORMAT_INDENT) > 0 && xb_silo_node_get_text_idx(sn) == XB_SILO_UNSET) { for (guint i = 0; i < helper->level; i++) g_string_append(helper->xml, " "); } g_string_append_printf(helper->xml, "</%s>", element_name); } /* add any optional tail */ if (xb_silo_node_get_tail_idx(sn) != XB_SILO_UNSET) { const gchar *tail_unsafe; g_autofree gchar *tail = NULL; tail_unsafe = xb_silo_from_strtab(self, xb_silo_node_get_tail_idx(sn), error); if (tail_unsafe == NULL) return FALSE; tail = xb_string_xml_escape(tail_unsafe); g_string_append(helper->xml, tail); } if (helper->flags & XB_NODE_EXPORT_FLAG_FORMAT_MULTILINE) g_string_append(helper->xml, "\n"); return TRUE; } /* private */ GString * xb_silo_export_with_root(XbSilo *self, XbSiloNode *sroot, XbNodeExportFlags flags, GError **error) { XbSiloNode *sn; XbSiloExportHelper helper = { .flags = flags, .level = 0, .off = sizeof(XbSiloHeader), }; g_return_val_if_fail(XB_IS_SILO(self), NULL); /* this implies the other */ if (flags & XB_NODE_EXPORT_FLAG_ONLY_CHILDREN) flags |= XB_NODE_EXPORT_FLAG_INCLUDE_SIBLINGS; /* optional subtree export */ if (sroot != NULL) { sn = sroot; if (sn != NULL && flags & XB_NODE_EXPORT_FLAG_ONLY_CHILDREN) { g_autoptr(GError) error_local = NULL; sn = xb_silo_get_child_node(self, sn, &error_local); if (sn == NULL) { if (!g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) { g_propagate_error(error, g_steal_pointer(&error_local)); return NULL; } } } } else { sn = xb_silo_get_root_node(self, error); if (sn == NULL) return NULL; } /* no root */ if (sn == NULL) { g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "no data to export"); return NULL; } /* root node */ helper.xml = g_string_new(NULL); if ((flags & XB_NODE_EXPORT_FLAG_ADD_HEADER) > 0) g_string_append(helper.xml, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); do { g_autoptr(GError) error_local = NULL; if (!xb_silo_export_node(self, &helper, sn, error)) { g_string_free(helper.xml, TRUE); return NULL; } if ((flags & XB_NODE_EXPORT_FLAG_INCLUDE_SIBLINGS) == 0) break; sn = xb_silo_get_next_node(self, sn, &error_local); if (sn == NULL) { if (g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) break; g_propagate_error(error, g_steal_pointer(&error_local)); return NULL; } } while (TRUE); /* success */ return helper.xml; } /** * xb_silo_export: * @self: a #XbSilo * @flags: some #XbNodeExportFlags, e.g. #XB_NODE_EXPORT_FLAG_NONE * @error: the #GError, or %NULL * * Exports the silo back to XML. * * Returns: XML data, or %NULL for an error * * Since: 0.1.0 **/ gchar * xb_silo_export(XbSilo *self, XbNodeExportFlags flags, GError **error) { GString *xml; g_return_val_if_fail(XB_IS_SILO(self), NULL); g_return_val_if_fail(error == NULL || *error == NULL, NULL); xml = xb_silo_export_with_root(self, NULL, flags, error); if (xml == NULL) return NULL; return g_string_free(xml, FALSE); } /** * xb_silo_export_file: * @self: a #XbSilo * @file: a #GFile * @flags: some #XbNodeExportFlags, e.g. #XB_NODE_EXPORT_FLAG_NONE * @cancellable: a #GCancellable, or %NULL * @error: the #GError, or %NULL * * Exports the silo back to an XML file. * * Returns: %TRUE on success * * Since: 0.1.2 **/ gboolean xb_silo_export_file(XbSilo *self, GFile *file, XbNodeExportFlags flags, GCancellable *cancellable, GError **error) { g_autoptr(GString) xml = NULL; g_return_val_if_fail(XB_IS_SILO(self), FALSE); g_return_val_if_fail(G_IS_FILE(file), FALSE); g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); xml = xb_silo_export_with_root(self, NULL, flags, error); if (xml == NULL) return FALSE; return g_file_replace_contents(file, xml->str, xml->len, NULL, /* etag */ FALSE, /* make-backup */ G_FILE_CREATE_NONE, NULL, /* new etag */ cancellable, error); }