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: [email protected], Debian Security Team 
> <[email protected]>
> 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
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 <[email protected]>  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 <[email protected]>
+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 <[email protected]>
+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

Reply via email to