Update module github.com/ulikunitz/xz to v0.5.15 [SECURITY]

Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
This commit is contained in:
renovate[bot]
2025-08-31 11:24:38 +00:00
committed by GitHub
parent 1666b135d4
commit 75eb26e6d1
7 changed files with 162 additions and 63 deletions

2
go.mod
View File

@@ -91,7 +91,7 @@ require (
github.com/sylabs/sif/v2 v2.21.1 // indirect
github.com/tchap/go-patricia/v2 v2.3.3 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/ulikunitz/xz v0.5.13 // indirect
github.com/ulikunitz/xz v0.5.15 // indirect
github.com/vbatts/tar-split v0.12.1 // indirect
github.com/vbauerster/mpb/v8 v8.10.2 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect

4
go.sum
View File

@@ -243,8 +243,8 @@ github.com/tchap/go-patricia/v2 v2.3.3/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwD
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0=
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs=
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.13 h1:ar98gWrjf4H1ev05fYP/o29PDZw9DrI3niHtnEqyuXA=
github.com/ulikunitz/xz v0.5.13/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo=
github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
github.com/vbauerster/mpb/v8 v8.10.2 h1:2uBykSHAYHekE11YvJhKxYmLATKHAGorZwFlyNw4hHM=

View File

@@ -1,9 +1,5 @@
# TODO list
## Release v0.5.x
1. Support check flag in gxz command.
## Release v0.6
1. Review encoder and check for lzma improvements under xz.
@@ -86,6 +82,13 @@
## Log
## 2025-08-28
Release v0.5.14 addresses the security vulnerability CVE-2025-58058. If you put
bytes in from of a LZMA stream, the header might not be read correctly and
memory for the dictionary buffer allocated. I have implemented mitigations for
the problem.
### 2025-08-20
Release v0.5.13 addressed issue #61 regarding handling of multiple WriteClosers

View File

@@ -60,36 +60,36 @@ const noHeaderSize uint64 = 1<<64 - 1
// HeaderLen provides the length of the LZMA file header.
const HeaderLen = 13
// header represents the header of an LZMA file.
type header struct {
properties Properties
dictCap int
// uncompressed size; negative value if no size is given
size int64
// Header represents the Header of an LZMA file.
type Header struct {
Properties Properties
DictSize uint32
// uncompressed Size; negative value if no Size is given
Size int64
}
// marshalBinary marshals the header.
func (h *header) marshalBinary() (data []byte, err error) {
if err = h.properties.verify(); err != nil {
func (h *Header) marshalBinary() (data []byte, err error) {
if err = h.Properties.verify(); err != nil {
return nil, err
}
if !(0 <= h.dictCap && int64(h.dictCap) <= MaxDictCap) {
if !(h.DictSize <= MaxDictCap) {
return nil, fmt.Errorf("lzma: DictCap %d out of range",
h.dictCap)
h.DictSize)
}
data = make([]byte, 13)
// property byte
data[0] = h.properties.Code()
data[0] = h.Properties.Code()
// dictionary capacity
putUint32LE(data[1:5], uint32(h.dictCap))
putUint32LE(data[1:5], uint32(h.DictSize))
// uncompressed size
var s uint64
if h.size > 0 {
s = uint64(h.size)
if h.Size > 0 {
s = uint64(h.Size)
} else {
s = noHeaderSize
}
@@ -99,20 +99,20 @@ func (h *header) marshalBinary() (data []byte, err error) {
}
// unmarshalBinary unmarshals the header.
func (h *header) unmarshalBinary(data []byte) error {
func (h *Header) unmarshalBinary(data []byte) error {
if len(data) != HeaderLen {
return errors.New("lzma.unmarshalBinary: data has wrong length")
}
// properties
var err error
if h.properties, err = PropertiesForCode(data[0]); err != nil {
if h.Properties, err = PropertiesForCode(data[0]); err != nil {
return err
}
// dictionary capacity
h.dictCap = int(uint32LE(data[1:]))
if h.dictCap < 0 {
h.DictSize = uint32LE(data[1:])
if int(h.DictSize) < 0 {
return errors.New(
"LZMA header: dictionary capacity exceeds maximum " +
"integer")
@@ -121,10 +121,10 @@ func (h *header) unmarshalBinary(data []byte) error {
// uncompressed size
s := uint64LE(data[5:])
if s == noHeaderSize {
h.size = -1
h.Size = -1
} else {
h.size = int64(s)
if h.size < 0 {
h.Size = int64(s)
if h.Size < 0 {
return errors.New(
"LZMA header: uncompressed size " +
"out of int64 range")
@@ -134,9 +134,9 @@ func (h *header) unmarshalBinary(data []byte) error {
return nil
}
// validDictCap checks whether the dictionary capacity is correct. This
// validDictSize checks whether the dictionary capacity is correct. This
// is used to weed out wrong file headers.
func validDictCap(dictcap int) bool {
func validDictSize(dictcap int) bool {
if int64(dictcap) == MaxDictCap {
return true
}
@@ -155,13 +155,16 @@ func validDictCap(dictcap int) bool {
// dictionary sizes of 2^n or 2^n+2^(n-1) with n >= 10 or 2^32-1. If
// there is an explicit size it must not exceed 256 GiB. The length of
// the data argument must be HeaderLen.
//
// This function should be disregarded because there is no guarantee that LZMA
// files follow the constraints.
func ValidHeader(data []byte) bool {
var h header
var h Header
if err := h.unmarshalBinary(data); err != nil {
return false
}
if !validDictCap(h.dictCap) {
if !validDictSize(int(h.DictSize)) {
return false
}
return h.size < 0 || h.size <= 1<<38
return h.Size < 0 || h.Size <= 1<<38
}

View File

@@ -6,25 +6,32 @@
// Reader and Writer support the classic LZMA format. Reader2 and
// Writer2 support the decoding and encoding of LZMA2 streams.
//
// The package is written completely in Go and doesn't rely on any external
// The package is written completely in Go and does not rely on any external
// library.
package lzma
import (
"errors"
"fmt"
"io"
)
// ReaderConfig stores the parameters for the reader of the classic LZMA
// format.
type ReaderConfig struct {
// Since v0.5.14 this parameter sets an upper limit for a .lzma file's
// dictionary size. This helps to mitigate problems with mangled
// headers.
DictCap int
}
// fill converts the zero values of the configuration to the default values.
func (c *ReaderConfig) fill() {
if c.DictCap == 0 {
c.DictCap = 8 * 1024 * 1024
// set an upper limit of 2 GiB-1 for dictionary capacity
// to address the zero prefix security issue.
c.DictCap = (1 << 31) - 1
// original: c.DictCap = 8 * 1024 * 1024
}
}
@@ -39,10 +46,33 @@ func (c *ReaderConfig) Verify() error {
}
// Reader provides a reader for LZMA files or streams.
//
// # Security concerns
//
// Note that LZMA format doesn't support a magic marker in the header. So
// [NewReader] cannot determine whether it reads the actual header. For instance
// the LZMA stream might have a zero byte in front of the reader, leading to
// larger dictionary sizes and file sizes. The code will detect later that there
// are problems with the stream, but the dictionary has already been allocated
// and this might consume a lot of memory.
//
// Version 0.5.14 introduces built-in mitigations:
//
// - The [ReaderConfig] DictCap field is now interpreted as a limit for the
// dictionary size.
// - The default is 2 Gigabytes minus 1 byte (2^31-1 bytes).
// - Users can check with the [Reader.Header] method what the actual values are in
// their LZMA files and set a smaller limit using [ReaderConfig].
// - The dictionary size doesn't exceed the larger of the file size and
// the minimum dictionary size. This is another measure to prevent huge
// memory allocations for the dictionary.
// - The code supports stream sizes only up to a pebibyte (1024^5).
type Reader struct {
lzma io.Reader
h header
d *decoder
lzma io.Reader
header Header
// headerOrig stores the original header read from the stream.
headerOrig Header
d *decoder
}
// NewReader creates a new reader for an LZMA stream using the classic
@@ -51,8 +81,37 @@ func NewReader(lzma io.Reader) (r *Reader, err error) {
return ReaderConfig{}.NewReader(lzma)
}
// ErrDictSize reports about an error of the dictionary size.
type ErrDictSize struct {
ConfigDictCap int
HeaderDictSize uint32
Message string
}
// Error returns the error message.
func (e *ErrDictSize) Error() string {
return e.Message
}
func newErrDictSize(messageformat string,
configDictCap int, headerDictSize uint32,
args ...interface{}) *ErrDictSize {
newArgs := make([]interface{}, len(args)+2)
newArgs[0] = configDictCap
newArgs[1] = headerDictSize
copy(newArgs[2:], args)
return &ErrDictSize{
ConfigDictCap: configDictCap,
HeaderDictSize: headerDictSize,
Message: fmt.Sprintf(messageformat, newArgs...),
}
}
// We support only files not larger than 1 << 50 bytes (a pebibyte, 1024^5).
const maxStreamSize = 1 << 50
// NewReader creates a new reader for an LZMA stream in the classic
// format. The function reads and verifies the the header of the LZMA
// format. The function reads and verifies the header of the LZMA
// stream.
func (c ReaderConfig) NewReader(lzma io.Reader) (r *Reader, err error) {
if err = c.Verify(); err != nil {
@@ -66,29 +125,63 @@ func (c ReaderConfig) NewReader(lzma io.Reader) (r *Reader, err error) {
return nil, err
}
r = &Reader{lzma: lzma}
if err = r.h.unmarshalBinary(data); err != nil {
if err = r.header.unmarshalBinary(data); err != nil {
return nil, err
}
if r.h.dictCap < MinDictCap {
r.h.dictCap = MinDictCap
r.headerOrig = r.header
dictSize := int64(r.header.DictSize)
if int64(c.DictCap) < dictSize {
return nil, newErrDictSize(
"lzma: header dictionary size %[2]d exceeds configured dictionary capacity %[1]d",
c.DictCap, uint32(dictSize),
)
}
dictCap := r.h.dictCap
if c.DictCap > dictCap {
dictCap = c.DictCap
if dictSize < MinDictCap {
dictSize = MinDictCap
}
// original code: disabled this because there is no point in increasing
// the dictionary above what is stated in the file.
/*
if int64(c.DictCap) > int64(dictSize) {
dictSize = int64(c.DictCap)
}
*/
size := r.header.Size
if size >= 0 && size < dictSize {
dictSize = size
}
// Protect against modified or malicious headers.
if size > maxStreamSize {
return nil, fmt.Errorf(
"lzma: stream size %d exceeds a pebibyte (1024^5)",
size)
}
if dictSize < MinDictCap {
dictSize = MinDictCap
}
state := newState(r.h.properties)
dict, err := newDecoderDict(dictCap)
r.header.DictSize = uint32(dictSize)
state := newState(r.header.Properties)
dict, err := newDecoderDict(int(dictSize))
if err != nil {
return nil, err
}
r.d, err = newDecoder(ByteReader(lzma), state, dict, r.h.size)
r.d, err = newDecoder(ByteReader(lzma), state, dict, r.header.Size)
if err != nil {
return nil, err
}
return r, nil
}
// Header returns the header as read from the LZMA stream. It is intended to
// allow the user to understand what parameters are typically provided in the
// headers of the LZMA files and set the DictCap field in [ReaderConfig]
// accordingly.
func (r *Reader) Header() (h Header, ok bool) {
return r.headerOrig, r.d != nil
}
// EOSMarker indicates that an EOS marker has been encountered.
func (r *Reader) EOSMarker() bool {
return r.d.eosMarker

View File

@@ -96,21 +96,21 @@ func (c *WriterConfig) Verify() error {
}
// header returns the header structure for this configuration.
func (c *WriterConfig) header() header {
h := header{
properties: *c.Properties,
dictCap: c.DictCap,
size: -1,
func (c *WriterConfig) header() Header {
h := Header{
Properties: *c.Properties,
DictSize: uint32(c.DictCap),
Size: -1,
}
if c.SizeInHeader {
h.size = c.Size
h.Size = c.Size
}
return h
}
// Writer writes an LZMA stream in the classic format.
type Writer struct {
h header
h Header
bw io.ByteWriter
buf *bufio.Writer
e *encoder
@@ -130,12 +130,12 @@ func (c WriterConfig) NewWriter(lzma io.Writer) (w *Writer, err error) {
w.buf = bufio.NewWriter(lzma)
w.bw = w.buf
}
state := newState(w.h.properties)
m, err := c.Matcher.new(w.h.dictCap)
state := newState(w.h.Properties)
m, err := c.Matcher.new(int(w.h.DictSize))
if err != nil {
return nil, err
}
dict, err := newEncoderDict(w.h.dictCap, c.BufSize, m)
dict, err := newEncoderDict(int(w.h.DictSize), c.BufSize, m)
if err != nil {
return nil, err
}
@@ -171,8 +171,8 @@ func (w *Writer) writeHeader() error {
// Write puts data into the Writer.
func (w *Writer) Write(p []byte) (n int, err error) {
if w.h.size >= 0 {
m := w.h.size
if w.h.Size >= 0 {
m := w.h.Size
m -= w.e.Compressed() + int64(w.e.dict.Buffered())
if m < 0 {
m = 0
@@ -192,9 +192,9 @@ func (w *Writer) Write(p []byte) (n int, err error) {
// Close closes the writer stream. It ensures that all data from the
// buffer will be compressed and the LZMA stream will be finished.
func (w *Writer) Close() error {
if w.h.size >= 0 {
if w.h.Size >= 0 {
n := w.e.Compressed() + int64(w.e.dict.Buffered())
if n != w.h.size {
if n != w.h.Size {
return errSize
}
}

2
vendor/modules.txt vendored
View File

@@ -308,7 +308,7 @@ github.com/tchap/go-patricia/v2/patricia
# github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399
## explicit
github.com/titanous/rocacheck
# github.com/ulikunitz/xz v0.5.13
# github.com/ulikunitz/xz v0.5.15
## explicit; go 1.12
github.com/ulikunitz/xz
github.com/ulikunitz/xz/internal/hash