untrusted comment: verify with openbsd-78-base.pub
RWS3/nvFmk4SWQCDRqPczeJHRA/22kAnvJHyDuDg9eaiSE2CayfFuuISrYKloVhkHRCa3MNh6QeXSCvwGtNjj1fhr78mCoEDJAg=

OpenBSD 7.8 errata 008, December 3, 2025:

Fix buffer overflow vulnerabilities in libpng which is part of
libfreetype.
CVE-2025-64505 CVE-2025-64506 CVE-2025-64720 CVE-2025-65018

Apply by doing:
    signify -Vep /etc/signify/openbsd-78-base.pub -x 008_libpng.patch.sig \
        -m - | (cd /usr/xenocara && patch -p0)

And then rebuild libpng and install libfreetype:
    cd /usr/xenocara/lib/libpng
    make -f Makefile.bsd-wrapper obj
    make -f Makefile.bsd-wrapper build
    cd /usr/xenocara/lib/freetype
    make obj
    make build

Index: lib/libpng/pngread.c
===================================================================
RCS file: /cvs/xenocara/lib/libpng/pngread.c,v
diff -u -p -r1.1.1.1 pngread.c
--- lib/libpng/pngread.c	17 Sep 2025 14:44:40 -0000	1.1.1.1
+++ lib/libpng/pngread.c	26 Nov 2025 22:56:34 -0000
@@ -3129,6 +3129,54 @@ png_image_read_colormapped(png_voidp arg
    }
 }
 
+/* Row reading for interlaced 16-to-8 bit depth conversion with local buffer. */
+static int
+png_image_read_direct_scaled(png_voidp argument)
+{
+   png_image_read_control *display = png_voidcast(png_image_read_control*,
+       argument);
+   png_imagep image = display->image;
+   png_structrp png_ptr = image->opaque->png_ptr;
+   png_bytep local_row = png_voidcast(png_bytep, display->local_row);
+   png_bytep first_row = png_voidcast(png_bytep, display->first_row);
+   ptrdiff_t row_bytes = display->row_bytes;
+   int passes;
+
+   /* Handle interlacing. */
+   switch (png_ptr->interlaced)
+   {
+      case PNG_INTERLACE_NONE:
+         passes = 1;
+         break;
+
+      case PNG_INTERLACE_ADAM7:
+         passes = PNG_INTERLACE_ADAM7_PASSES;
+         break;
+
+      default:
+         png_error(png_ptr, "unknown interlace type");
+   }
+
+   /* Read each pass using local_row as intermediate buffer. */
+   while (--passes >= 0)
+   {
+      png_uint_32 y = image->height;
+      png_bytep output_row = first_row;
+
+      for (; y > 0; --y)
+      {
+         /* Read into local_row (gets transformed 8-bit data). */
+         png_read_row(png_ptr, local_row, NULL);
+
+         /* Copy from local_row to user buffer. */
+         memcpy(output_row, local_row, (size_t)row_bytes);
+         output_row += row_bytes;
+      }
+   }
+
+   return 1;
+}
+
 /* Just the row reading part of png_image_read. */
 static int
 png_image_read_composite(png_voidp argument)
@@ -3547,6 +3595,7 @@ png_image_read_direct(png_voidp argument
    int linear = (format & PNG_FORMAT_FLAG_LINEAR) != 0;
    int do_local_compose = 0;
    int do_local_background = 0; /* to avoid double gamma correction bug */
+   int do_local_scale = 0; /* for interlaced 16-to-8 bit conversion */
    int passes = 0;
 
    /* Add transforms to ensure the correct output format is produced then check
@@ -3680,8 +3729,16 @@ png_image_read_direct(png_voidp argument
             png_set_expand_16(png_ptr);
 
          else /* 8-bit output */
+         {
             png_set_scale_16(png_ptr);
 
+            /* For interlaced images, use local_row buffer to avoid overflow
+             * in png_combine_row() which writes using IHDR bit-depth.
+             */
+            if (png_ptr->interlaced != 0)
+               do_local_scale = 1;
+         }
+
          change &= ~PNG_FORMAT_FLAG_LINEAR;
       }
 
@@ -3951,6 +4008,24 @@ png_image_read_direct(png_voidp argument
 
       display->local_row = row;
       result = png_safe_execute(image, png_image_read_background, display);
+      display->local_row = NULL;
+      png_free(png_ptr, row);
+
+      return result;
+   }
+
+   else if (do_local_scale != 0)
+   {
+      /* For interlaced 16-to-8 conversion, use an intermediate row buffer
+       * to avoid buffer overflows in png_combine_row. The local_row is sized
+       * for the transformed (8-bit) output, preventing the overflow that would
+       * occur if png_combine_row wrote 16-bit data directly to the user buffer.
+       */
+      int result;
+      png_voidp row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr));
+
+      display->local_row = row;
+      result = png_safe_execute(image, png_image_read_direct_scaled, display);
       display->local_row = NULL;
       png_free(png_ptr, row);
 
Index: lib/libpng/pngrtran.c
===================================================================
RCS file: /cvs/xenocara/lib/libpng/pngrtran.c,v
diff -u -p -r1.1.1.1 pngrtran.c
--- lib/libpng/pngrtran.c	17 Sep 2025 14:44:40 -0000	1.1.1.1
+++ lib/libpng/pngrtran.c	26 Nov 2025 22:56:35 -0000
@@ -501,9 +501,19 @@ png_set_quantize(png_structrp png_ptr, p
    {
       int i;
 
+      /* Initialize the array to index colors.
+       *
+       * Ensure quantize_index can fit 256 elements (PNG_MAX_PALETTE_LENGTH)
+       * rather than num_palette elements. This is to prevent buffer overflows
+       * caused by malformed PNG files with out-of-range palette indices.
+       *
+       * Be careful to avoid leaking memory. Applications are allowed to call
+       * this function more than once per png_struct.
+       */
+      png_free(png_ptr, png_ptr->quantize_index);
       png_ptr->quantize_index = (png_bytep)png_malloc(png_ptr,
-          (png_alloc_size_t)num_palette);
-      for (i = 0; i < num_palette; i++)
+          PNG_MAX_PALETTE_LENGTH);
+      for (i = 0; i < PNG_MAX_PALETTE_LENGTH; i++)
          png_ptr->quantize_index[i] = (png_byte)i;
    }
 
@@ -1774,19 +1784,51 @@ png_init_read_transformations(png_struct
                   }
                   else /* if (png_ptr->trans_alpha[i] != 0xff) */
                   {
-                     png_byte v, w;
-
-                     v = png_ptr->gamma_to_1[palette[i].red];
-                     png_composite(w, v, png_ptr->trans_alpha[i], back_1.red);
-                     palette[i].red = png_ptr->gamma_from_1[w];
-
-                     v = png_ptr->gamma_to_1[palette[i].green];
-                     png_composite(w, v, png_ptr->trans_alpha[i], back_1.green);
-                     palette[i].green = png_ptr->gamma_from_1[w];
-
-                     v = png_ptr->gamma_to_1[palette[i].blue];
-                     png_composite(w, v, png_ptr->trans_alpha[i], back_1.blue);
-                     palette[i].blue = png_ptr->gamma_from_1[w];
+                     if ((png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0)
+                     {
+                        /* Premultiply only:
+                         * component = round((component * alpha) / 255)
+                         */
+                        png_uint_32 component;
+
+                        component = png_ptr->gamma_to_1[palette[i].red];
+                        component =
+                            (component * png_ptr->trans_alpha[i] + 128) / 255;
+                        palette[i].red = png_ptr->gamma_from_1[component];
+
+                        component = png_ptr->gamma_to_1[palette[i].green];
+                        component =
+                            (component * png_ptr->trans_alpha[i] + 128) / 255;
+                        palette[i].green = png_ptr->gamma_from_1[component];
+
+                        component = png_ptr->gamma_to_1[palette[i].blue];
+                        component =
+                            (component * png_ptr->trans_alpha[i] + 128) / 255;
+                        palette[i].blue = png_ptr->gamma_from_1[component];
+                     }
+                     else
+                     {
+                        /* Composite with background color:
+                         * component =
+                         *    alpha * component + (1 - alpha) * background
+                         */
+                        png_byte v, w;
+
+                        v = png_ptr->gamma_to_1[palette[i].red];
+                        png_composite(w, v,
+                            png_ptr->trans_alpha[i], back_1.red);
+                        palette[i].red = png_ptr->gamma_from_1[w];
+
+                        v = png_ptr->gamma_to_1[palette[i].green];
+                        png_composite(w, v,
+                            png_ptr->trans_alpha[i], back_1.green);
+                        palette[i].green = png_ptr->gamma_from_1[w];
+
+                        v = png_ptr->gamma_to_1[palette[i].blue];
+                        png_composite(w, v,
+                            png_ptr->trans_alpha[i], back_1.blue);
+                        palette[i].blue = png_ptr->gamma_from_1[w];
+                     }
                   }
                }
                else
Index: lib/libpng/pngwrite.c
===================================================================
RCS file: /cvs/xenocara/lib/libpng/pngwrite.c,v
diff -u -p -r1.1.1.1 pngwrite.c
--- lib/libpng/pngwrite.c	17 Sep 2025 14:44:40 -0000	1.1.1.1
+++ lib/libpng/pngwrite.c	26 Nov 2025 22:56:35 -0000
@@ -2173,8 +2173,7 @@ png_image_write_main(png_voidp argument)
     * before it is written.  This only applies when the input is 16-bit and
     * either there is an alpha channel or it is converted to 8-bit.
     */
-   if ((linear != 0 && alpha != 0 ) ||
-       (colormap == 0 && display->convert_to_8bit != 0))
+   if (linear != 0 && (alpha != 0 || display->convert_to_8bit != 0))
    {
       png_bytep row = png_voidcast(png_bytep, png_malloc(png_ptr,
           png_get_rowbytes(png_ptr, info_ptr)));
