| From e494a2c3a76b752f6008486fbb36a59f833666c0 Mon Sep 17 00:00:00 2001 |
| From: Toddr Bot <toddbot@rinaldo.us> |
| Date: Mon, 16 Mar 2026 22:16:11 +0000 |
| Subject: [PATCH] fix: off-by-one heap buffer overflow in st_serial_stack |
| growth check |
| |
| When st_serial_stackptr == st_serial_stacksize - 1, the old check |
| (stackptr >= stacksize) would not trigger reallocation. The subsequent |
| ++stackptr then writes at index stacksize, one element past the |
| allocated buffer. |
| |
| Fix by checking stackptr + 1 >= stacksize so the buffer is grown |
| before the pre-increment write. |
| |
| Add a deep nesting test (600 levels) to exercise this code path. |
| |
| Fixes #39 |
| |
| Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
| |
| CVE: CVE-2006-10003 |
| |
| Upstream-Status: Backport [https://github.com/cpan-authors/XML-Parser/commit/3eb9cc95420fa0c3f76947c4708962546bf27cfd.patch] |
| |
| Signed-off-by: Bai, Haiqing <Haiqing.Bai@windriver.com> |
| --- |
| Expat/Expat.xs | 2 +- |
| t/deep_nesting.t | 22 ++++++++++++++++++++++ |
| 2 files changed, 23 insertions(+), 1 deletion(-) |
| create mode 100644 t/deep_nesting.t |
| |
| diff --git a/Expat/Expat.xs b/Expat/Expat.xs |
| index dbad380..f04a0cf 100644 |
| --- a/Expat/Expat.xs |
| +++ b/Expat/Expat.xs |
| @@ -499,7 +499,7 @@ startElement(void *userData, const char *name, const char **atts) |
| } |
| } |
| |
| - if (cbv->st_serial_stackptr >= cbv->st_serial_stacksize) { |
| + if (cbv->st_serial_stackptr + 1 >= cbv->st_serial_stacksize) { |
| unsigned int newsize = cbv->st_serial_stacksize + 512; |
| |
| Renew(cbv->st_serial_stack, newsize, unsigned int); |
| diff --git a/t/deep_nesting.t b/t/deep_nesting.t |
| new file mode 100644 |
| index 0000000..8237b5f |
| --- /dev/null |
| +++ b/t/deep_nesting.t |
| @@ -0,0 +1,22 @@ |
| +BEGIN { print "1..1\n"; } |
| + |
| +# Test for deeply nested elements to exercise st_serial_stack reallocation. |
| +# This catches off-by-one errors in the stack growth check (GH #39). |
| + |
| +use XML::Parser; |
| + |
| +my $depth = 600; |
| + |
| +my $xml = ''; |
| +for my $i (1 .. $depth) { |
| + $xml .= "<e$i>"; |
| +} |
| +for my $i (reverse 1 .. $depth) { |
| + $xml .= "</e$i>"; |
| +} |
| + |
| +my $p = XML::Parser->new; |
| +eval { $p->parse($xml) }; |
| + |
| +print "not " if $@; |
| +print "ok 1\n"; |
| -- |
| 2.49.1 |
| |