[pkg-gnupg-maint] Bug#1124221: gnupg2: CVE-2025-68973
Salvatore Bonaccorso
carnil at debian.org
Sun Dec 28 21:37:58 GMT 2025
Hi
On Sun, Dec 28, 2025 at 09:37:51PM +0100, Salvatore Bonaccorso wrote:
> Source: gnupg2
> Version: 2.4.8-4
> Severity: important
> Tags: security upstream
> X-Debbugs-Cc: carnil at debian.org, Debian Security Team <team at security.debian.org>
> Control: found -1 2.4.7-21
> Control: found -1 2.2.40-1.1+deb12u1
> Control: found -1 2.2.40-1.1
>
> Hi,
>
> The following vulnerability was published for gnupg2.
>
> CVE-2025-68973[0]:
> | In GnuPG through 2.4.8, armor_filter in g10/armor.c has two
> | increments of an index variable where one is intended, leading to an
> | out-of-bounds write for crafted input.
>
>
> If you fix the vulnerability please also make sure to include the
> CVE (Common Vulnerabilities & Exposures) id in your changelog entry.
>
> For further information see:
>
> [0] https://security-tracker.debian.org/tracker/CVE-2025-68973
> https://www.cve.org/CVERecord?id=CVE-2025-68973
> [1] https://github.com/gpg/gnupg/commit/115d138ba599328005c5321c0ef9f00355838ca9
Proposed changes (not in form of a NMU) are in
https://salsa.debian.org/debian/gnupg2/-/merge_requests/20
(and debdiff attached with the NMU entry, but this is just a proposal
for the changes, I'm really not sure if you prefer to skip the comment
changes, I choosed this strategy to make the upstream commit apply
and there are no functional changes by applying the first commit).
It is as well tested on debusine:
https://debusine.debian.net/debian/developers/work-request/297747/
Please count for now this just really as proposal of changes.
Regards,
Salvatore
-------------- next part --------------
diff -Nru gnupg2-2.4.8/debian/changelog gnupg2-2.4.8/debian/changelog
--- gnupg2-2.4.8/debian/changelog 2025-10-22 22:39:23.000000000 +0200
+++ gnupg2-2.4.8/debian/changelog 2025-12-28 22:15:23.000000000 +0100
@@ -1,3 +1,12 @@
+gnupg2 (2.4.8-4.1) unstable; urgency=medium
+
+ * Non-maintainer upload.
+ * common: Reformat some comments in iobuf.c
+ * gpg: Fix possible memory corruption in the armor parser (CVE-2025-68973)
+ (Closes: #1124221)
+
+ -- Salvatore Bonaccorso <carnil at debian.org> Sun, 28 Dec 2025 22:15:23 +0100
+
gnupg2 (2.4.8-4) unstable; urgency=medium
* Make OpenPGP compliance mode require a self-sig for each UID
diff -Nru gnupg2-2.4.8/debian/patches/common-Reformat-some-comments-in-iobuf.c.patch gnupg2-2.4.8/debian/patches/common-Reformat-some-comments-in-iobuf.c.patch
--- gnupg2-2.4.8/debian/patches/common-Reformat-some-comments-in-iobuf.c.patch 1970-01-01 01:00:00.000000000 +0100
+++ gnupg2-2.4.8/debian/patches/common-Reformat-some-comments-in-iobuf.c.patch 2025-12-28 22:13:27.000000000 +0100
@@ -0,0 +1,515 @@
+From: Werner Koch <wk at gnupg.org>
+Date: Fri, 10 Oct 2025 10:28:03 +0200
+Subject: common: Reformat some comments in iobuf.c
+Origin: https://github.com/gpg/gnupg/commit/d61546521b71deb2ba89b31c71f963c76d51f19f
+
+--
+
+Comments with stars on the left side are easier to read. Also a
+long comment in the form
+
+ if (...)
+ /* This is a comment
+ * for the next block */
+ {
+ ...
+ }
+
+makes it hard to see the start of a block or hides a forgotten block.
+In contrast to GNU common use we don't want this but put the comment
+either into the block or with adjusted wording above the condition.
+---
+ common/iobuf.c | 297 +++++++++++++++++++++++++------------------------
+ 1 file changed, 151 insertions(+), 146 deletions(-)
+
+diff --git a/common/iobuf.c b/common/iobuf.c
+index 770b67c29d69..8a128b3f6c5d 100644
+--- a/common/iobuf.c
++++ b/common/iobuf.c
+@@ -1720,49 +1720,49 @@ iobuf_push_filter2 (iobuf_t a,
+ }
+
+ /* We want to create a new filter and put it in front of A. A
+- simple implementation would do:
+-
+- b = iobuf_alloc (...);
+- b->chain = a;
+- return a;
+-
+- This is a bit problematic: A is the head of the pipeline and
+- there are potentially many pointers to it. Requiring the caller
+- to update all of these pointers is a burden.
+-
+- An alternative implementation would add a level of indirection.
+- For instance, we could use a pipeline object, which contains a
+- pointer to the first filter in the pipeline. This is not what we
+- do either.
+-
+- Instead, we allocate a new buffer (B) and copy the first filter's
+- state into that and use the initial buffer (A) for the new
+- filter. One limitation of this approach is that it is not
+- practical to maintain a pointer to a specific filter's state.
+-
+- Before:
+-
+- A
+- |
+- v 0x100 0x200
+- +----------+ +----------+
+- | filter x |--------->| filter y |---->....
+- +----------+ +----------+
+-
+- After: B
+- |
+- v 0x300
+- +----------+
+- A | filter x |
+- | +----------+
+- v 0x100 ^ v 0x200
+- +----------+ +----------+
+- | filter w | | filter y |---->....
+- +----------+ +----------+
+-
+- Note: filter x's address changed from 0x100 to 0x300, but A still
+- points to the head of the pipeline.
+- */
++ * simple implementation would do:
++ *
++ * b = iobuf_alloc (...);
++ * b->chain = a;
++ * return a;
++ *
++ * This is a bit problematic: A is the head of the pipeline and
++ * there are potentially many pointers to it. Requiring the caller
++ * to update all of these pointers is a burden.
++ *
++ * An alternative implementation would add a level of indirection.
++ * For instance, we could use a pipeline object, which contains a
++ * pointer to the first filter in the pipeline. This is not what we
++ * do either.
++ *
++ * Instead, we allocate a new buffer (B) and copy the first filter's
++ * state into that and use the initial buffer (A) for the new
++ * filter. One limitation of this approach is that it is not
++ * practical to maintain a pointer to a specific filter's state.
++ *
++ * Before:
++ *
++ * A
++ * |
++ * v 0x100 0x200
++ * +----------+ +----------+
++ * | filter x |--------->| filter y |---->....
++ * +----------+ +----------+
++ *
++ * After: B
++ * |
++ * v 0x300
++ * +----------+
++ * A | filter x |
++ * | +----------+
++ * v 0x100 ^ v 0x200
++ * +----------+ +----------+
++ * | filter w | | filter y |---->....
++ * +----------+ +----------+
++ *
++ * Note: filter x's address changed from 0x100 to 0x300, but A still
++ * points to the head of the pipeline.
++ */
+
+ b = xmalloc (sizeof *b);
+ memcpy (b, a, sizeof *b);
+@@ -1776,51 +1776,51 @@ iobuf_push_filter2 (iobuf_t a,
+ a->filter_ov_owner = 0;
+ a->filter_eof = 0;
+ if (a->use == IOBUF_OUTPUT_TEMP)
+- /* A TEMP filter buffers any data sent to it; it does not forward
+- any data down the pipeline. If we add a new filter to the
+- pipeline, it shouldn't also buffer data. It should send it
+- downstream to be buffered. Thus, the correct type for a filter
+- added in front of an IOBUF_OUTPUT_TEMP filter is IOBUF_OUPUT, not
+- IOBUF_OUTPUT_TEMP. */
+ {
++ /* A TEMP filter buffers any data sent to it; it does not
++ * forward any data down the pipeline. If we add a new filter
++ * to the pipeline, it shouldn't also buffer data. It should
++ * send it downstream to be buffered. Thus, the correct type
++ * for a filter added in front of an IOBUF_OUTPUT_TEMP filter is
++ * IOBUF_OUPUT, not IOBUF_OUTPUT_TEMP. */
+ a->use = IOBUF_OUTPUT;
+
+ /* When pipeline is written to, the temp buffer's size is
+- increased accordingly. We don't need to allocate a 10 MB
+- buffer for a non-terminal filter. Just use the default
+- size. */
++ * increased accordingly. We don't need to allocate a 10 MB
++ * buffer for a non-terminal filter. Just use the default
++ * size. */
+ a->d.size = iobuf_buffer_size;
+ }
+ else if (a->use == IOBUF_INPUT_TEMP)
+- /* Same idea as above. */
+ {
++ /* Same idea as above. */
+ a->use = IOBUF_INPUT;
+ a->d.size = iobuf_buffer_size;
+ }
+
+ /* The new filter (A) gets a new buffer.
+-
+- If the pipeline is an output or temp pipeline, then giving the
+- buffer to the new filter means that data that was written before
+- the filter was pushed gets sent to the filter. That's clearly
+- wrong.
+-
+- If the pipeline is an input pipeline, then giving the buffer to
+- the new filter (A) means that data that has read from (B), but
+- not yet read from the pipeline won't be processed by the new
+- filter (A)! That's certainly not what we want. */
++ *
++ * If the pipeline is an output or temp pipeline, then giving the
++ * buffer to the new filter means that data that was written before
++ * the filter was pushed gets sent to the filter. That's clearly
++ * wrong.
++ *
++ * If the pipeline is an input pipeline, then giving the buffer to
++ * the new filter (A) means that data that has read from (B), but
++ * not yet read from the pipeline won't be processed by the new
++ * filter (A)! That's certainly not what we want. */
+ a->d.buf = xmalloc (a->d.size);
+ a->d.len = 0;
+ a->d.start = 0;
+
+- /* disable nlimit for the new stream */
++ /* Disable nlimit for the new stream. */
+ a->ntotal = b->ntotal + b->nbytes;
+ a->nlimit = a->nbytes = 0;
+ a->nofast = 0;
+- /* make a link from the new stream to the original stream */
++ /* Make a link from the new stream to the original stream. */
+ a->chain = b;
+
+- /* setup the function on the new stream */
++ /* Setup the function on the new stream. */
+ a->filter = f;
+ a->filter_ov = ov;
+ a->filter_ov_owner = rel_ov;
+@@ -1835,13 +1835,14 @@ iobuf_push_filter2 (iobuf_t a,
+ print_chain (a);
+ }
+
+- /* now we can initialize the new function if we have one */
++ /* Now we can initialize the new function if we have one. */
+ if (a->filter && (rc = a->filter (a->filter_ov, IOBUFCTRL_INIT, a->chain,
+ NULL, &dummy_len)))
+ log_error ("IOBUFCTRL_INIT failed: %s\n", gpg_strerror (rc));
+ return rc;
+ }
+
++
+ /****************
+ * Remove an i/o filter.
+ */
+@@ -1865,7 +1866,7 @@ iobuf_pop_filter (iobuf_t a, int (*f) (void *opaque, int control,
+ return 0;
+ }
+ if (!a->filter)
+- { /* this is simple */
++ { /* (this is simple) */
+ b = a->chain;
+ log_assert (b);
+ xfree (a->d.buf);
+@@ -1880,14 +1881,14 @@ iobuf_pop_filter (iobuf_t a, int (*f) (void *opaque, int control,
+ if (!b)
+ log_bug ("iobuf_pop_filter(): filter function not found\n");
+
+- /* flush this stream if it is an output stream */
++ /* Flush this stream if it is an output stream ... */
+ if (a->use == IOBUF_OUTPUT && (rc = filter_flush (b)))
+ {
+ log_error ("filter_flush failed in iobuf_pop_filter: %s\n",
+ gpg_strerror (rc));
+ return rc;
+ }
+- /* and tell the filter to free it self */
++ /* and tell the filter to free it self */
+ if (b->filter && (rc = b->filter (b->filter_ov, IOBUFCTRL_FREE, b->chain,
+ NULL, &dummy_len)))
+ {
+@@ -1905,10 +1906,9 @@ iobuf_pop_filter (iobuf_t a, int (*f) (void *opaque, int control,
+ if (a == b && !b->chain)
+ log_bug ("can't remove the last filter from the chain\n");
+ else if (a == b)
+- { /* remove the first iobuf from the chain */
+- /* everything from b is copied to a. This is save because
+- * a flush has been done on the to be removed entry
+- */
++ { /* Remove the first iobuf from the chain.
++ * Everything from B is copied to A. This is save because
++ * a flush has been done on the to be removed entry. */
+ b = a->chain;
+ xfree (a->d.buf);
+ xfree (a->real_fname);
+@@ -1918,11 +1918,11 @@ iobuf_pop_filter (iobuf_t a, int (*f) (void *opaque, int control,
+ log_debug ("iobuf-%d.%d: popped filter\n", a->no, a->subno);
+ }
+ else if (!b->chain)
+- { /* remove the last iobuf from the chain */
++ { /* Remove the last iobuf from the chain. */
+ log_bug ("Ohh jeee, trying to remove a head filter\n");
+ }
+ else
+- { /* remove an intermediate iobuf from the chain */
++ { /* Remove an intermediate iobuf from the chain. */
+ log_bug ("Ohh jeee, trying to remove an intermediate filter\n");
+ }
+
+@@ -1958,17 +1958,19 @@ underflow_target (iobuf_t a, int clear_pending_eof, size_t target)
+ (int) (a->d.size - (a->d.len - a->d.start)));
+
+ if (a->use == IOBUF_INPUT_TEMP)
+- /* By definition, there isn't more data to read into the
+- buffer. */
+- return -1;
++ {
++ /* By definition, there isn't more data to read into the
++ buffer. */
++ return -1;
++ }
+
+ log_assert (a->use == IOBUF_INPUT);
+
+ a->e_d.used = 0;
+
+ /* If there is still some buffered data, then move it to the start
+- of the buffer and try to fill the end of the buffer. (This is
+- useful if we are called from iobuf_peek().) */
++ * of the buffer and try to fill the end of the buffer. (This is
++ * useful if we are called from iobuf_peek().) */
+ log_assert (a->d.start <= a->d.len);
+ a->d.len -= a->d.start;
+ if (a->d.len)
+@@ -1976,11 +1978,11 @@ underflow_target (iobuf_t a, int clear_pending_eof, size_t target)
+ a->d.start = 0;
+
+ if (a->d.len < target && a->filter_eof)
+- /* The last time we tried to read from this filter, we got an EOF.
+- We couldn't return the EOF, because there was buffered data.
+- Since there is no longer any buffered data, return the
+- error. */
+ {
++ /* The last time we tried to read from this filter, we got an
++ * EOF. We couldn't return the EOF, because there was buffered
++ * data. Since there is no longer any buffered data, return the
++ * error. */
+ if (DBG_IOBUF)
+ log_debug ("iobuf-%d.%d: underflow: eof (pending eof)\n",
+ a->no, a->subno);
+@@ -1988,8 +1990,8 @@ underflow_target (iobuf_t a, int clear_pending_eof, size_t target)
+ return -1;
+
+ if (a->chain)
+- /* A filter follows this one. Free this filter. */
+ {
++ /* A filter follows this one. Free this filter. */
+ iobuf_t b = a->chain;
+ if (DBG_IOBUF)
+ log_debug ("iobuf-%d.%d: filter popped (pending EOF returned)\n",
+@@ -2006,11 +2008,11 @@ underflow_target (iobuf_t a, int clear_pending_eof, size_t target)
+ }
+
+ if (a->d.len == 0 && a->error)
+- /* The last time we tried to read from this filter, we got an
+- error. We couldn't return the error, because there was
+- buffered data. Since there is no longer any buffered data,
+- return the error. */
+ {
++ /* The last time we tried to read from this filter, we got an
++ * error. We couldn't return the error, because there was
++ * buffered data. Since there is no longer any buffered data,
++ * return the error. */
+ if (DBG_IOBUF)
+ log_debug ("iobuf-%d.%d: pending error (%s) returned\n",
+ a->no, a->subno, gpg_strerror (a->error));
+@@ -2018,10 +2020,10 @@ underflow_target (iobuf_t a, int clear_pending_eof, size_t target)
+ }
+
+ if (a->filter && ! a->filter_eof && ! a->error)
+- /* We have a filter function and the last time we tried to read we
+- didn't get an EOF or an error. Try to fill the buffer. */
+ {
+- /* Be careful to account for any buffered data. */
++ /* We have a filter function and the last time we tried to read
++ * we didn't get an EOF or an error. Try to fill the buffer.
++ * Be careful to account for any buffered data. */
+ len = a->d.size - a->d.len;
+
+ if (a->e_d.preferred && a->d.len < IOBUF_ZEROCOPY_THRESHOLD_SIZE
+@@ -2034,51 +2036,54 @@ underflow_target (iobuf_t a, int clear_pending_eof, size_t target)
+ }
+
+ if (len == 0)
+- /* There is no space for more data. Don't bother calling
+- A->FILTER. */
+- rc = 0;
++ {
++ /* There is no space for more data. Don't bother calling
++ * A->FILTER. */
++ rc = 0;
++ }
+ else
+- {
+- /* If no buffered data and drain buffer has been setup, and drain
+- * buffer is largish, read data directly to drain buffer. */
+- if (a->d.len == 0
+- && a->e_d.buf
+- && a->e_d.len >= IOBUF_ZEROCOPY_THRESHOLD_SIZE)
+- {
+- len = a->e_d.len;
+-
+- if (DBG_IOBUF)
+- log_debug ("iobuf-%d.%d: underflow: A->FILTER (%lu bytes, to external drain)\n",
+- a->no, a->subno, (ulong)len);
++ {
++ /* If no buffered data and drain buffer has been setup, and
++ * drain buffer is largish, read data directly to drain buffer. */
++ if (a->d.len == 0
++ && a->e_d.buf
++ && a->e_d.len >= IOBUF_ZEROCOPY_THRESHOLD_SIZE)
++ {
++ len = a->e_d.len;
+
+- rc = a->filter (a->filter_ov, IOBUFCTRL_UNDERFLOW, a->chain,
+- a->e_d.buf, &len);
+- a->e_d.used = len;
+- len = 0;
+- }
+- else
+- {
+- if (DBG_IOBUF)
+- log_debug ("iobuf-%d.%d: underflow: A->FILTER (%lu bytes)\n",
+- a->no, a->subno, (ulong)len);
++ if (DBG_IOBUF)
++ log_debug ("iobuf-%d.%d: underflow:"
++ " A->FILTER (%lu bytes, to external drain)\n",
++ a->no, a->subno, (ulong)len);
++
++ rc = a->filter (a->filter_ov, IOBUFCTRL_UNDERFLOW, a->chain,
++ a->e_d.buf, &len);
++ a->e_d.used = len;
++ len = 0;
++ }
++ else
++ {
++ if (DBG_IOBUF)
++ log_debug ("iobuf-%d.%d: underflow: A->FILTER (%lu bytes)\n",
++ a->no, a->subno, (ulong)len);
+
+- rc = a->filter (a->filter_ov, IOBUFCTRL_UNDERFLOW, a->chain,
+- &a->d.buf[a->d.len], &len);
+- }
+- }
++ rc = a->filter (a->filter_ov, IOBUFCTRL_UNDERFLOW, a->chain,
++ &a->d.buf[a->d.len], &len);
++ }
++ }
+ a->d.len += len;
+
+ if (DBG_IOBUF)
+- log_debug ("iobuf-%d.%d: A->FILTER() returned rc=%d (%s), read %lu bytes%s\n",
++ log_debug ("iobuf-%d.%d: A->FILTER() returned rc=%d (%s),"
++ " read %lu bytes%s\n",
+ a->no, a->subno,
+ rc, rc == 0 ? "ok" : rc == -1 ? "EOF" : gpg_strerror (rc),
+ (ulong)(a->e_d.used ? a->e_d.used : len),
+ a->e_d.used ? " (to external buffer)" : "");
+-/* if( a->no == 1 ) */
+-/* log_hexdump (" data:", a->d.buf, len); */
++ /* if ( a->no == 1 ) */
++ /* log_hexdump (" data:", a->d.buf, len); */
+
+- if (rc == -1)
+- /* EOF. */
++ if (rc == -1) /* EOF. */
+ {
+ size_t dummy_len = 0;
+
+@@ -2096,39 +2101,40 @@ underflow_target (iobuf_t a, int clear_pending_eof, size_t target)
+
+ if (clear_pending_eof && a->d.len == 0 && a->e_d.used == 0
+ && a->chain)
+- /* We don't need to keep this filter around at all:
+-
+- - we got an EOF
+- - we have no buffered data
+- - a filter follows this one.
+-
+- Unlink this filter. */
+ {
++ /* We don't need to keep this filter around at all:
++ *
++ * - we got an EOF
++ * - we have no buffered data
++ * - a filter follows this one.
++ *
++ * Unlink this filter. */
+ iobuf_t b = a->chain;
+ if (DBG_IOBUF)
+- log_debug ("iobuf-%d.%d: pop in underflow (nothing buffered, got EOF)\n",
+- a->no, a->subno);
++ log_debug ("iobuf-%d.%d: pop in underflow"
++ " (nothing buffered, got EOF)\n", a->no, a->subno);
+ xfree (a->d.buf);
+ xfree (a->real_fname);
+ memcpy (a, b, sizeof *a);
+ xfree (b);
+
+ print_chain (a);
+-
+ return -1;
+ }
+ else if (a->d.len == 0 && a->e_d.used == 0)
+- /* We can't unlink this filter (it is the only one in the
+- pipeline), but we can immediately return EOF. */
+- return -1;
++ {
++ /* We can't unlink this filter (it is the only one in
++ * the pipeline), but we can immediately return EOF. */
++ return -1;
++ }
++
+ }
+- else if (rc)
+- /* Record the error. */
++ else if (rc) /* Record the error. */
+ {
+ a->error = rc;
+
++ /* If there is no buffered data, immediately return EOF. */
+ if (a->d.len == 0 && a->e_d.used == 0)
+- /* There is no buffered data. Immediately return EOF. */
+ return -1;
+ }
+ }
+@@ -2139,8 +2145,7 @@ underflow_target (iobuf_t a, int clear_pending_eof, size_t target)
+ if (a->d.start < a->d.len)
+ return a->d.buf[a->d.start++];
+
+- /* EOF. */
+- return -1;
++ return -1; /* EOF. */
+ }
+
+
+@@ -2156,7 +2161,7 @@ filter_flush (iobuf_t a)
+ a->e_d.used = 0;
+
+ if (a->use == IOBUF_OUTPUT_TEMP)
+- { /* increase the temp buffer */
++ { /* Increase the temp buffer. */
+ size_t newsize = a->d.size + iobuf_buffer_size;
+
+ if (DBG_IOBUF)
+--
+2.51.0
+
diff -Nru gnupg2-2.4.8/debian/patches/gpg-Fix-possible-memory-corruption-in-the-armor-pars.patch gnupg2-2.4.8/debian/patches/gpg-Fix-possible-memory-corruption-in-the-armor-pars.patch
--- gnupg2-2.4.8/debian/patches/gpg-Fix-possible-memory-corruption-in-the-armor-pars.patch 1970-01-01 01:00:00.000000000 +0100
+++ gnupg2-2.4.8/debian/patches/gpg-Fix-possible-memory-corruption-in-the-armor-pars.patch 2025-12-28 22:13:44.000000000 +0100
@@ -0,0 +1,107 @@
+From: Werner Koch <wk at gnupg.org>
+Date: Thu, 23 Oct 2025 11:36:04 +0200
+Subject: gpg: Fix possible memory corruption in the armor parser.
+Origin: https://github.com/gpg/gnupg/commit/115d138ba599328005c5321c0ef9f00355838ca9
+Bug-Debian: https://bugs.debian.org/1124221
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2025-68973
+
+* g10/armor.c (armor_filter): Fix faulty double increment.
+
+* common/iobuf.c (underflow_target): Assert that the filter
+implementations behave well.
+--
+
+This fixes a bug in a code path which can only be reached with special
+crafted input data and would then error out at an upper layer due to
+corrupt input (every second byte in the buffer is unitialized
+garbage). No fuzzing has yet hit this case and we don't have a test
+case for this code path. However memory corruption can never be
+tolerated as it always has the protential for remode code execution.
+
+Reported-by: 8b79fe4dd0581c1cd000e1fbecba9f39e16a396a
+Fixes-commit: c27c7416d5148865a513e007fb6f0a34993a6073
+which fixed
+Fixes-commit: 7d0efec7cf5ae110c99511abc32587ff0c45b14f
+
+The bug was introduced on 1999-01-07 by me:
+* armor.c: Rewrote large parts.
+which I fixed on 1999-03-02 but missed to fix the other case:
+* armor.c (armor_filter): Fixed armor bypassing.
+
+Below is base64+gzipped test data which can be used with valgrind to
+show access to uninitalized memory in write(2) in the unpatched code.
+
+--8<---------------cut here---------------start------------->8---
+H4sICIDd+WgCA3h4AO3QMQ6CQBCG0djOKbY3G05gscYFSRAJt/AExp6Di0cQG0ze
+a//MV0zOq3Pt+jFN3ZTKfLvP9ZLafqifJUe8juOjeZbVtSkbRPmRgICAgICAgICA
+gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA
+gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA
+gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA
+gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA
+gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA
+gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA
+gICAgICAgICAgICAgICAgICAgICAgICAgMCXF6dYDgAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC7E14AAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwZ94aieId3+8EAA==
+--8<---------------cut here---------------end--------------->8---
+---
+ common/iobuf.c | 6 ++++++
+ g10/armor.c | 4 ++--
+ 2 files changed, 8 insertions(+), 2 deletions(-)
+
+diff --git a/common/iobuf.c b/common/iobuf.c
+index 8a128b3f6c5d..769df958ded3 100644
+--- a/common/iobuf.c
++++ b/common/iobuf.c
+@@ -2043,6 +2043,8 @@ underflow_target (iobuf_t a, int clear_pending_eof, size_t target)
+ }
+ else
+ {
++ size_t tmplen;
++
+ /* If no buffered data and drain buffer has been setup, and
+ * drain buffer is largish, read data directly to drain buffer. */
+ if (a->d.len == 0
+@@ -2056,8 +2058,10 @@ underflow_target (iobuf_t a, int clear_pending_eof, size_t target)
+ " A->FILTER (%lu bytes, to external drain)\n",
+ a->no, a->subno, (ulong)len);
+
++ tmplen = len; /* Used to check for bugs in the filter. */
+ rc = a->filter (a->filter_ov, IOBUFCTRL_UNDERFLOW, a->chain,
+ a->e_d.buf, &len);
++ log_assert (len <= tmplen);
+ a->e_d.used = len;
+ len = 0;
+ }
+@@ -2067,8 +2071,10 @@ underflow_target (iobuf_t a, int clear_pending_eof, size_t target)
+ log_debug ("iobuf-%d.%d: underflow: A->FILTER (%lu bytes)\n",
+ a->no, a->subno, (ulong)len);
+
++ tmplen = len;
+ rc = a->filter (a->filter_ov, IOBUFCTRL_UNDERFLOW, a->chain,
+ &a->d.buf[a->d.len], &len);
++ log_assert (len <= tmplen);
+ }
+ }
+ a->d.len += len;
+diff --git a/g10/armor.c b/g10/armor.c
+index 036b72772717..59a6202aa4bc 100644
+--- a/g10/armor.c
++++ b/g10/armor.c
+@@ -1312,8 +1312,8 @@ armor_filter( void *opaque, int control,
+ n = 0;
+ if( afx->buffer_len ) {
+ /* Copy the data from AFX->BUFFER to BUF. */
+- for(; n < size && afx->buffer_pos < afx->buffer_len; n++ )
+- buf[n++] = afx->buffer[afx->buffer_pos++];
++ for(; n < size && afx->buffer_pos < afx->buffer_len;)
++ buf[n++] = afx->buffer[afx->buffer_pos++];
+ if( afx->buffer_pos >= afx->buffer_len )
+ afx->buffer_len = 0;
+ }
+--
+2.51.0
+
diff -Nru gnupg2-2.4.8/debian/patches/series gnupg2-2.4.8/debian/patches/series
--- gnupg2-2.4.8/debian/patches/series 2025-10-22 22:39:23.000000000 +0200
+++ gnupg2-2.4.8/debian/patches/series 2025-12-28 22:13:56.000000000 +0100
@@ -43,3 +43,5 @@
debian-packaging/no-keyboxd.patch
gpgv-Avoid-Assuan-and-NPth-dependencies.patch
nl.po.update.diff
+common-Reformat-some-comments-in-iobuf.c.patch
+gpg-Fix-possible-memory-corruption-in-the-armor-pars.patch
More information about the pkg-gnupg-maint
mailing list