Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package kitty for openSUSE:Factory checked in at 2026-07-02 20:12:34 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/kitty (Old) and /work/SRC/openSUSE:Factory/.kitty.new.1982 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "kitty" Thu Jul 2 20:12:34 2026 rev:64 rq:1363531 version:0.47.4 Changes: -------- --- /work/SRC/openSUSE:Factory/kitty/kitty.changes 2026-06-16 14:00:23.726691053 +0200 +++ /work/SRC/openSUSE:Factory/.kitty.new.1982/kitty.changes 2026-07-02 20:16:22.551743959 +0200 @@ -1,0 +2,8 @@ +Mon Jun 29 19:21:03 UTC 2026 - Scott Bradnick <[email protected]> + +- Addressing bsc#1269601 -> CVE-2026-46604 + * https://github.com/kovidgoyal/kitty/commit/351ee9fcbd692abd3bfbf2cf4296f69522e3571a + * Adjusted kitty-0.47.4.tar.gz to point to: + golang.org/x/image v0.43.0 and refreshed vendor.tar.gz + +------------------------------------------------------------------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ kitty.spec ++++++ --- /var/tmp/diff_new_pack.QHr8mV/_old 2026-07-02 20:16:23.427774261 +0200 +++ /var/tmp/diff_new_pack.QHr8mV/_new 2026-07-02 20:16:23.427774261 +0200 @@ -25,11 +25,14 @@ License: GPL-3.0-only Group: System/X11/Terminals URL: https://github.com/kovidgoyal/kitty -Source: https://github.com/kovidgoyal/kitty/archive/v%{version}.tar.gz#/%{name}-%{version}.tar.gz +### TEMPORARILY USE ADJUSTED TARBALL FOR golang.org/x/image v0.43.0 [CVE-2026-46604] +#Source: https://github.com/kovidgoyal/kitty/archive/v%%{version}.tar.gz#/%%{name}-%%{version}.tar.gz +Source: %{name}-%{version}.tar.gz ##### Source1: vendor.tar.gz Source2: kitty-rpmlintrc Patch0: buildmode-and-skip_docs.diff +#Patch1: 351ee9f-smb.diff BuildRequires: ImageMagick-devel BuildRequires: Mesa-libGL-devel BuildRequires: fdupes ++++++ kitty-0.47.4.tar.gz ++++++ /work/SRC/openSUSE:Factory/kitty/kitty-0.47.4.tar.gz /work/SRC/openSUSE:Factory/.kitty.new.1982/kitty-0.47.4.tar.gz differ: char 4, line 1 ++++++ vendor.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/golang.org/x/image/bmp/reader.go new/vendor/golang.org/x/image/bmp/reader.go --- old/vendor/golang.org/x/image/bmp/reader.go 2026-06-15 04:41:46.000000000 +0200 +++ new/vendor/golang.org/x/image/bmp/reader.go 2026-06-29 21:41:52.000000000 +0200 @@ -12,6 +12,8 @@ "image" "image/color" "io" + + "golang.org/x/image/internal/safemath" ) // ErrUnsupported means that the input BMP image uses a valid but unsupported @@ -190,6 +192,16 @@ if width < 0 || height < 0 { return image.Config{}, 0, false, false, ErrUnsupported } + if (width == 0) != (height == 0) { + // We'll take 0x0, but Nx0 or 0xN is suspicious. + return image.Config{}, 0, false, false, ErrUnsupported + } + // Check that the image fits in memory. + // This conservatively assumes 4 bytes per pixel, + // rather than using the actual pixel size. + if _, ok := safemath.Mul3(width, height, 4); !ok { + return image.Config{}, 0, false, false, ErrUnsupported + } // We only support 1 plane and 8, 24 or 32 bits per pixel and no // compression. planes, bpp, compression := readUint16(b[26:28]), readUint16(b[28:30]), readUint32(b[30:34]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/golang.org/x/image/internal/safemath/safemath.go new/vendor/golang.org/x/image/internal/safemath/safemath.go --- old/vendor/golang.org/x/image/internal/safemath/safemath.go 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/golang.org/x/image/internal/safemath/safemath.go 2026-06-29 21:41:52.000000000 +0200 @@ -0,0 +1,31 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package safemath contains overflow-safe math functions. +package safemath + +import "math/bits" + +// Mul3 returns (x * y * z), unless at least one argument is negative or if the +// computation overflows the int type. +func Mul3(x, y, z int) (_ int, ok bool) { + // The following is copied from image/geom.go in std, + // but returns an explicit ok/not-ok rather than using -1 to indicate an error. + if x < 0 || y < 0 || z < 0 { + return -1, false + } + hi, lo := bits.Mul64(uint64(x), uint64(y)) + if hi != 0 { + return -1, false + } + hi, lo = bits.Mul64(lo, uint64(z)) + if hi != 0 { + return -1, false + } + a := int(lo) + if a < 0 || uint64(a) != lo { + return -1, false + } + return a, true +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/golang.org/x/image/tiff/buffer.go new/vendor/golang.org/x/image/tiff/buffer.go --- old/vendor/golang.org/x/image/tiff/buffer.go 2026-06-15 04:41:46.000000000 +0200 +++ new/vendor/golang.org/x/image/tiff/buffer.go 2026-06-29 21:41:52.000000000 +0200 @@ -5,7 +5,9 @@ package tiff import ( + "fmt" "io" + "math" "slices" ) @@ -35,22 +37,34 @@ } func (b *buffer) ReadAt(p []byte, off int64) (int, error) { - o := int(off) - end := o + len(p) - if int64(end) != off+int64(len(p)) { + if off < 0 { + // Impossible in correct usage, but check for safety. + return 0, fmt.Errorf("invalid ReadAt offset %v (bug)", off) + } + end64 := off + int64(len(p)) + if end64 < off || end64 > math.MaxInt { return 0, io.ErrUnexpectedEOF } + end := int(end64) err := b.fill(end) end = min(end, len(b.buf)) - return copy(p, b.buf[min(o, end):end]), err + return copy(p, b.buf[min(int(off), end):end]), err } // Slice returns a slice of the underlying buffer. The slice contains // n bytes starting at offset off. -func (b *buffer) Slice(off, n int) ([]byte, error) { +func (b *buffer) Slice(off, n int64) ([]byte, error) { + if off < 0 || n < 0 { + // Impossible in correct usage, but check for safety. + return nil, fmt.Errorf("invalid negative input to Slice(%v, %v) (bug)", off, n) + } end := off + n - if err := b.fill(end); err != nil { + if end < 0 || end > math.MaxInt { + // end is too large. Treat this as a read error. + return nil, io.ErrUnexpectedEOF + } + if err := b.fill(int(end)); err != nil { return nil, err } return b.buf[off:end], nil diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/golang.org/x/image/tiff/reader.go new/vendor/golang.org/x/image/tiff/reader.go --- old/vendor/golang.org/x/image/tiff/reader.go 2026-06-15 04:41:46.000000000 +0200 +++ new/vendor/golang.org/x/image/tiff/reader.go 2026-06-29 21:41:52.000000000 +0200 @@ -11,7 +11,6 @@ "bytes" "compress/zlib" "encoding/binary" - "errors" "fmt" "image" "image/color" @@ -19,6 +18,7 @@ "math" "golang.org/x/image/ccitt" + "golang.org/x/image/internal/safemath" "golang.org/x/image/tiff/lzw" ) @@ -95,6 +95,7 @@ mode imageMode bpp uint features map[int][]uint + ifd map[int][ifdLen]byte palette []color.Color buf []byte @@ -113,9 +114,23 @@ return f[0] } +// firstIntVal returns the first int of the features entry with the given tag, +// or 0 if the tag does not exist. +// If the tag overflows an int, it returns an error. +func (d *decoder) firstIntVal(tag int) (int, error) { + v := d.firstVal(tag) + if v > math.MaxInt { + return 0, FormatError("IFD value too large") + } + return int(v), nil +} + // ifdUint decodes the IFD entry in p, which must be of the Byte, Short // or Long type, and returns the decoded uint values. -func (d *decoder) ifdUint(p []byte) (u []uint, err error) { +// +// maxCount limits the number of values. +// If the entry contains more than maxCount values, only the first maxCount are parsed. +func (d *decoder) ifdUint(p []byte, maxCount int) (u []uint, err error) { var raw []byte if len(p) < ifdLen { return nil, FormatError("bad IFD entry") @@ -130,9 +145,11 @@ if count > math.MaxInt32/lengths[datatype] { return nil, FormatError("IFD data too large") } + truncatedCount := min(int(count), maxCount) if datalen := lengths[datatype] * count; datalen > 4 { + truncatedLen := uint64(lengths[datatype]) * uint64(truncatedCount) // The IFD contains a pointer to the real value. - raw, err = safeReadAt(d.r, uint64(datalen), int64(d.byteOrder.Uint32(p[8:12]))) + raw, err = safeReadAt(d.r, truncatedLen, int64(d.byteOrder.Uint32(p[8:12]))) } else { raw = p[8 : 8+datalen] } @@ -140,18 +157,18 @@ return nil, err } - u = make([]uint, count) + u = make([]uint, truncatedCount) switch datatype { case dtByte: - for i := uint32(0); i < count; i++ { + for i := range u { u[i] = uint(raw[i]) } case dtShort: - for i := uint32(0); i < count; i++ { + for i := range u { u[i] = uint(d.byteOrder.Uint16(raw[2*i : 2*(i+1)])) } case dtLong: - for i := uint32(0); i < count; i++ { + for i := range u { u[i] = uint(d.byteOrder.Uint32(raw[4*i : 4*(i+1)])) } default: @@ -160,10 +177,30 @@ return u, nil } +// parseIFDOffsets parses an IFD entry stored in d.ifd using ifdUint. +// For IFD entries which can be large, we delay reading and parsing the entry +// until we know the image size, which lets us set reasonable bounds on the IFD entry. +func (d *decoder) parseIFDOffsets(tag int, maxCount int) (u []uint, err error) { + p, ok := d.ifd[tag] + if !ok { + return nil, nil + } + return d.ifdUint(p[:], maxCount) +} + // parseIFD decides whether the IFD entry in p is "interesting" and // stows away the data in the decoder. It returns the tag number of the // entry and an error, if any. func (d *decoder) parseIFD(p []byte) (int, error) { + // smallEntryMaxCount is the limit to use for parsed IFD entries that + // don't scale with the image size. + // + // We could be more precise and limit the number of entries to exactly the + // correct limit (for example, tTileWidth should always have exactly one value), + // but 16 is small enough to prevent excessive allocation and large enough to + // allow for any multi-value entry. + const smallEntryMaxCount = 16 + tag := d.byteOrder.Uint16(p[0:2]) switch tag { case tBitsPerSample, @@ -171,30 +208,36 @@ tPhotometricInterpretation, tCompression, tPredictor, - tStripOffsets, - tStripByteCounts, tRowsPerStrip, tTileWidth, tTileLength, - tTileOffsets, - tTileByteCounts, tImageLength, tImageWidth, tFillOrder, tT4Options, tT6Options: - val, err := d.ifdUint(p) + val, err := d.ifdUint(p, smallEntryMaxCount) if err != nil { return 0, err } d.features[int(tag)] = val + case tStripOffsets, + tStripByteCounts, + tTileOffsets, + tTileByteCounts: + // These keys may contain many values. + // Stash the IFD entry for later parsing. + var v [ifdLen]byte + copy(v[:], p) + d.ifd[int(tag)] = v case tColorMap: - val, err := d.ifdUint(p) + const maxColors = 256 + val, err := d.ifdUint(p, (3*maxColors)+1) if err != nil { return 0, err } numcolors := len(val) / 3 - if len(val)%3 != 0 || numcolors <= 0 || numcolors > 256 { + if len(val)%3 != 0 || numcolors <= 0 || numcolors > maxColors { return 0, FormatError("bad ColorMap length") } d.palette = make([]color.Color, numcolors) @@ -211,7 +254,7 @@ // the value is not 1 [= unsigned integer data], a Baseline // TIFF reader that cannot handle the SampleFormat value // must terminate the import process gracefully. - val, err := d.ifdUint(p) + val, err := d.ifdUint(p, smallEntryMaxCount) if err != nil { return 0, err } @@ -450,10 +493,15 @@ return nil } +// maxBytesPerPixel is the maximum possible bytes-per-pixel, +// used for conservative bounds checking. +const maxBytesPerPixel = 8 + func newDecoder(r io.Reader) (*decoder, error) { d := &decoder{ r: newReaderAt(r), features: make(map[int][]uint), + ifd: make(map[int][ifdLen]byte), } p := make([]byte, 8) @@ -499,10 +547,20 @@ prevTag = tag } - d.config.Width = int(d.firstVal(tImageWidth)) - d.config.Height = int(d.firstVal(tImageLength)) + if d.config.Width, err = d.firstIntVal(tImageWidth); err != nil { + return nil, err + } + if d.config.Height, err = d.firstIntVal(tImageLength); err != nil { + return nil, err + } if d.config.Width == 0 || d.config.Height == 0 { - return nil, errors.New("tiff: zero-size image") + return nil, FormatError("zero-size image") + } + // Check that the image fits in memory. + // This conservatively assumes 8 bytes per pixel, + // rather than using the actual pixel size. + if _, ok := safemath.Mul3(d.config.Width, d.config.Height, maxBytesPerPixel); !ok { + return nil, FormatError("image too large") } if _, ok := d.features[tBitsPerSample]; !ok { @@ -640,11 +698,15 @@ var blockOffsets, blockCounts []uint - if int(d.firstVal(tTileWidth)) != 0 { + if d.firstVal(tTileWidth) != 0 { blockPadding = true - blockWidth = int(d.firstVal(tTileWidth)) - blockHeight = int(d.firstVal(tTileLength)) + if blockWidth, err = d.firstIntVal(tTileWidth); err != nil { + return nil, err + } + if blockHeight, err = d.firstIntVal(tTileLength); err != nil { + return nil, err + } // The specification says that tile widths and lengths must be a multiple of 16. // We currently permit invalid sizes, but reject anything too small to limit the @@ -652,7 +714,30 @@ if blockWidth < 8 || blockHeight < 8 { return nil, FormatError("tile size is too small") } - + // Same conservative assumption on bytes-per-pixel as for the image dimensions. + if _, ok := safemath.Mul3(blockWidth, blockHeight, maxBytesPerPixel); !ok { + return nil, FormatError("tile size is too large") + } + if blockWidth-d.config.Width > 16 || blockHeight-d.config.Height > 16 { + // Tiles may be padded to the nearest multiple of 16, but one of + // the dimensions of the tile exceeds the image dimension by more + // than padding would require. + // + // Typical TIFF tiles are 256x256, but 1024x1024 appears to be + // in occasional use. If the tile is both larger than the image + // and has more than 1024 pixels in one dimension, it's + // probably malicious input. + // + // Note that this still permits very large tiles, + // so long as the tile is smaller than the image dimensions. + // A gigapixel image with a 2048x2048 tile size + // (used in some GIS applications) + // will be valid because the image dimensions are much larger + // than the tile size. + if blockWidth > 1024 || blockHeight > 1024 { + return nil, FormatError("tile size exceeds image size") + } + } if blockWidth != 0 { blocksAcross = (d.config.Width + blockWidth - 1) / blockWidth } @@ -660,20 +745,34 @@ blocksDown = (d.config.Height + blockHeight - 1) / blockHeight } - blockCounts = d.features[tTileByteCounts] - blockOffsets = d.features[tTileOffsets] - + blockOffsets, err = d.parseIFDOffsets(tTileOffsets, blocksAcross*blocksDown) + if err != nil { + return nil, err + } + blockCounts, err = d.parseIFDOffsets(tTileByteCounts, blocksAcross*blocksDown) + if err != nil { + return nil, err + } } else { - if int(d.firstVal(tRowsPerStrip)) != 0 { - blockHeight = int(d.firstVal(tRowsPerStrip)) + + if v := d.firstVal(tRowsPerStrip); v > 0 && v < uint(blockHeight) { + blockHeight = int(v) } if blockHeight != 0 { + // This can't overflow: We require that w*h*8 not overflow and + // blockHeight is no more than d.config.Height. blocksDown = (d.config.Height + blockHeight - 1) / blockHeight } - blockOffsets = d.features[tStripOffsets] - blockCounts = d.features[tStripByteCounts] + blockOffsets, err = d.parseIFDOffsets(tStripOffsets, blocksDown) + if err != nil { + return nil, err + } + blockCounts, err = d.parseIFDOffsets(tStripByteCounts, blocksDown) + if err != nil { + return nil, err + } } // Check if we have the right number of strips/tiles, offsets and counts. @@ -728,8 +827,11 @@ // but some tools interpret a missing Compression value as none, so we do // the same. case cNone, 0: + if n > blockMaxDataSize { + return nil, FormatError("block data size too large") + } if b, ok := d.r.(*buffer); ok { - d.buf, err = b.Slice(int(offset), int(n)) + d.buf, err = b.Slice(offset, n) } else { d.buf, err = safeReadAt(d.r, uint64(n), offset) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/golang.org/x/image/webp/decode.go new/vendor/golang.org/x/image/webp/decode.go --- old/vendor/golang.org/x/image/webp/decode.go 2026-06-15 04:41:46.000000000 +0200 +++ new/vendor/golang.org/x/image/webp/decode.go 2026-06-29 21:41:52.000000000 +0200 @@ -82,6 +82,9 @@ if err != nil { return nil, image.Config{}, err } + if seenVP8X && (fh.Width != int(widthMinusOne)+1 || fh.Height != int(heightMinusOne)+1) { + return nil, image.Config{}, errInvalidFormat + } if configOnly { return nil, image.Config{ ColorModel: color.YCbCrModel, @@ -111,7 +114,16 @@ return nil, c, err } m, err := vp8l.Decode(chunkData) - return m, image.Config{}, err + if err != nil { + return nil, image.Config{}, err + } + if seenVP8X { + bounds := m.Bounds() + if bounds.Dx() != int(widthMinusOne)+1 || bounds.Dy() != int(heightMinusOne)+1 { + return nil, image.Config{}, errInvalidFormat + } + } + return m, image.Config{}, nil case fccVP8X: if seenVP8X { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/modules.txt new/vendor/modules.txt --- old/vendor/modules.txt 2026-06-15 04:41:46.000000000 +0200 +++ new/vendor/modules.txt 2026-06-29 21:41:52.000000000 +0200 @@ -152,10 +152,11 @@ golang.org/x/exp/constraints golang.org/x/exp/rand golang.org/x/exp/slices -# golang.org/x/image v0.42.0 +# golang.org/x/image v0.43.0 ## explicit; go 1.25.0 golang.org/x/image/bmp golang.org/x/image/ccitt +golang.org/x/image/internal/safemath golang.org/x/image/riff golang.org/x/image/tiff golang.org/x/image/tiff/lzw
