Skip to content

Commit 2e94721

Browse files
committed
Improve error handling of libxml2 calls in xml.c
This commit fixes some defects in the backend's xml.c, found upon inspection of the internals of libxml2: - xmlEncodeSpecialChars() can fail on malloc(), returning NULL back to the caller. xmltext() assumed that this could never happen. Like other code paths, a TRY/CATCH block is added there, covering also the fact that cstring_to_text_with_len() could fail a memory allocation, where the backend would miss to free the buffer allocated by xmlEncodeSpecialChars(). - Some libxml2 routines called in xmlelement() can return NULL, like xmlAddChildList() or xmlTextWriterStartElement(). Dedicated errors are added for them. - xml_xmlnodetoxmltype() missed that xmlXPathCastNodeToString() can fail on an allocation failure. In this case, the call can just be moved to the existing TRY/CATCH block. All these code paths would cause the server to crash. As this is unlikely a problem in practice, no backpatch is done. Jim and I have caught these defects, not sure who has scored the most. The contrib module xml2/ has similar defects, which will be addressed in a separate change. Reported-by: Jim Jones <jim.jones@uni-muenster.de> Reviewed-by: Jim Jones <jim.jones@uni-muenster.de> Discussion: https://postgr.es/m/aEEingzOta_S_Nu7@paquier.xyz
1 parent 0836683 commit 2e94721

File tree

1 file changed

+62
-16
lines changed
  • src/backend/utils/adt

1 file changed

+62
-16
lines changed

src/backend/utils/adt/xml.c

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -529,14 +529,36 @@ xmltext(PG_FUNCTION_ARGS)
529529
#ifdef USE_LIBXML
530530
text *arg = PG_GETARG_TEXT_PP(0);
531531
text *result;
532-
xmlChar *xmlbuf = NULL;
532+
volatile xmlChar *xmlbuf = NULL;
533+
PgXmlErrorContext *xmlerrcxt;
534+
535+
/* Otherwise, we gotta spin up some error handling. */
536+
xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
533537

534-
xmlbuf = xmlEncodeSpecialChars(NULL, xml_text2xmlChar(arg));
538+
PG_TRY();
539+
{
540+
xmlbuf = xmlEncodeSpecialChars(NULL, xml_text2xmlChar(arg));
535541

536-
Assert(xmlbuf);
542+
if (xmlbuf == NULL || xmlerrcxt->err_occurred)
543+
xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
544+
"could not allocate xmlChar");
545+
546+
result = cstring_to_text_with_len((const char *) xmlbuf,
547+
xmlStrlen((const xmlChar *) xmlbuf));
548+
}
549+
PG_CATCH();
550+
{
551+
if (xmlbuf)
552+
xmlFree((xmlChar *) xmlbuf);
553+
554+
pg_xml_done(xmlerrcxt, true);
555+
PG_RE_THROW();
556+
}
557+
PG_END_TRY();
558+
559+
xmlFree((xmlChar *) xmlbuf);
560+
pg_xml_done(xmlerrcxt, false);
537561

538-
result = cstring_to_text_with_len((const char *) xmlbuf, xmlStrlen(xmlbuf));
539-
xmlFree(xmlbuf);
540562
PG_RETURN_XML_P(result);
541563
#else
542564
NO_XML_SUPPORT();
@@ -770,7 +792,10 @@ xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent)
770792
if (oldroot != NULL)
771793
xmlFreeNode(oldroot);
772794

773-
xmlAddChildList(root, content_nodes);
795+
if (xmlAddChildList(root, content_nodes) == NULL ||
796+
xmlerrcxt->err_occurred)
797+
xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
798+
"could not append xml node list");
774799

775800
/*
776801
* We use this node to insert newlines in the dump. Note: in at
@@ -931,27 +956,41 @@ xmlelement(XmlExpr *xexpr,
931956
xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
932957
"could not allocate xmlTextWriter");
933958

934-
xmlTextWriterStartElement(writer, (xmlChar *) xexpr->name);
959+
if (xmlTextWriterStartElement(writer, (xmlChar *) xexpr->name) < 0 ||
960+
xmlerrcxt->err_occurred)
961+
xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
962+
"could not start xml element");
935963

936964
forboth(arg, named_arg_strings, narg, xexpr->arg_names)
937965
{
938966
char *str = (char *) lfirst(arg);
939967
char *argname = strVal(lfirst(narg));
940968

941969
if (str)
942-
xmlTextWriterWriteAttribute(writer,
943-
(xmlChar *) argname,
944-
(xmlChar *) str);
970+
{
971+
if (xmlTextWriterWriteAttribute(writer,
972+
(xmlChar *) argname,
973+
(xmlChar *) str) < 0 ||
974+
xmlerrcxt->err_occurred)
975+
xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
976+
"could not write xml attribute");
977+
}
945978
}
946979

947980
foreach(arg, arg_strings)
948981
{
949982
char *str = (char *) lfirst(arg);
950983

951-
xmlTextWriterWriteRaw(writer, (xmlChar *) str);
984+
if (xmlTextWriterWriteRaw(writer, (xmlChar *) str) < 0 ||
985+
xmlerrcxt->err_occurred)
986+
xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
987+
"could not write raw xml text");
952988
}
953989

954-
xmlTextWriterEndElement(writer);
990+
if (xmlTextWriterEndElement(writer) < 0 ||
991+
xmlerrcxt->err_occurred)
992+
xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
993+
"could not end xml element");
955994

956995
/* we MUST do this now to flush data out to the buffer ... */
957996
xmlFreeTextWriter(writer);
@@ -4220,20 +4259,27 @@ xml_xmlnodetoxmltype(xmlNodePtr cur, PgXmlErrorContext *xmlerrcxt)
42204259
}
42214260
else
42224261
{
4223-
xmlChar *str;
4262+
volatile xmlChar *str = NULL;
42244263

4225-
str = xmlXPathCastNodeToString(cur);
42264264
PG_TRY();
42274265
{
4266+
char *escaped;
4267+
4268+
str = xmlXPathCastNodeToString(cur);
4269+
if (str == NULL || xmlerrcxt->err_occurred)
4270+
xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
4271+
"could not allocate xmlChar");
4272+
42284273
/* Here we rely on XML having the same representation as TEXT */
4229-
char *escaped = escape_xml((char *) str);
4274+
escaped = escape_xml((char *) str);
42304275

42314276
result = (xmltype *) cstring_to_text(escaped);
42324277
pfree(escaped);
42334278
}
42344279
PG_FINALLY();
42354280
{
4236-
xmlFree(str);
4281+
if (str)
4282+
xmlFree((xmlChar *) str);
42374283
}
42384284
PG_END_TRY();
42394285
}

0 commit comments

Comments
 (0)