| From: | Jim Jones <jim(dot)jones(at)uni-muenster(dot)de> |
|---|---|
| To: | PostgreSQL Hackers <pgsql-hackers(at)postgresql(dot)org> |
| Subject: | XML functions can exhaust backend memory with libxml2 < 2.11 |
| Date: | 2026-11-11 11:06:56 |
| Message-ID: | b0036c7b-8ba3-4dda-9544-08bcf3170e4c@uni-muenster.de |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
Hi
xml_errorHandler() appends every libxml2 error to err_buf without a
limit. libxml2 caps reported parser errors at 100 (XML_MAX_ERRORS [1,2])
since 2.11, but older versions, still shipped by current distributions,
report them all. A document with one non-fatal namespace error per
element can make err_buf reach MaxAllocSize, and enlargeStringInfo()
then raises ERROR from inside the libxml2 callback. The longjmp abandons
the document libxml2 was building, so it is not freed until the session
ends.
Any user can trigger it with xpath(), xmlexists() or xmltable. Depending
on the host, a few calls get the backend killed by the OOM killer. I was
able to reproduce it with the postgres:18 docker image:
$ podman run -d --name pgtest --memory 3g --memory-swap 3g -e
POSTGRES_HOST_AUTH_METHOD=trust postgres:18 -c logging_collector=on
18b8de253790882cbbf3a0f63828d1ea136723f6d2e7a3f3379cf8e079b08f71
$ podman exec -itu postgres pgtest psql
psql (18.6 (Debian 18.6-1.pgdg13+2))
Type "help" for help.
postgres=# select xpath('/r', ('<r>' || repeat('<p:a/>', 5200000) ||
'</r>')::xml) is not null;
ERROR: string buffer exceeds maximum allowed length (1073741823 bytes)
DETAIL: Cannot enlarge string buffer containing 1073741630 bytes by 208
more bytes.
postgres=# select xpath('/r', ('<r>' || repeat('<p:a/>', 5200000) ||
'</r>')::xml) is not null;
ERROR: string buffer exceeds maximum allowed length (1073741823 bytes)
DETAIL: Cannot enlarge string buffer containing 1073741630 bytes by 208
more bytes.
postgres=# select xpath('/r', ('<r>' || repeat('<p:a/>', 5200000) ||
'</r>')::xml) is not null;
server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.
The connection to the server was lost. Attempting reset: Failed.
The connection to the server was lost. Attempting reset: Failed.
!?> \! grep oom_kill /sys/fs/cgroup/memory.events
oom_kill 1
!?> \! grep -h "terminated by signal" $PGDATA/log/*.log
2026-11-11 10:18:22.585 UTC [1] LOG: client backend (PID 76) was
terminated by signal 9: Killed
!?>
The attached patch stops collecting messages after 100, the same limit
libxml2 2.11+ applies itself with XML_MAX_ERRORS, but I am not sure this
is the right way to fix it. Any suggestions on this approach?
Best, Jim
1 - https://github.com/GNOME/libxml2/blob/master/parserInternals.c#L54
2 -
https://github.com/GNOME/libxml2/commit/59b33661784359c6d3a8309ddbd2129fb2688548
| Attachment | Content-Type | Size |
|---|---|---|
| v1-0001-Limit-the-number-of-libxml2-messages-collected-in.patch | text/x-patch | 3.3 KB |
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Bilal Kahraman | 2026-11-11 11:17:19 | [PATCH v2] Fix SP-GiST index-only scans that return only INCLUDE columns |
| Previous Message | Ayush Tiwari | 2026-11-11 10:56:29 | Re: fix special case during recovery for hot_standby=off |