Read xz with a real dictionary maximum (review 65, hq issue 345)
mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request

ulikunitz/xz's ReaderConfig.DictCap is a minimum: a stream asking for a
1 GiB or 4 GiB dictionary was given it. github.com/therootcompany/xz, a port
of xz-embedded, takes a maximum and refuses a block whose LZMA2 dictionary is
larger before allocating it. 64 MiB, xz's largest preset.
This commit is contained in:
jochen
2026-10-12 15:39:34 +02:00
parent 10f76cafc4
commit 01fc9e1d73
64 changed files with 3342 additions and 7679 deletions
+62
View File
@@ -6,8 +6,10 @@ import (
"compress/gzip"
"context"
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"fmt"
"hash/crc32"
"io"
"net"
"net/http"
@@ -15,6 +17,7 @@ import (
"os"
"os/exec"
"path/filepath"
"runtime"
"sort"
"strings"
"testing"
@@ -405,3 +408,62 @@ func TestAnXzReleaseIsReadWithoutAnyXzToolOnTheBuildSeat(t *testing.T) {
t.Fatalf("%v", e)
}
}
// withDictionary is an xz stream whose first block's LZMA2 dictionary property is props, its header's CRC32
// recomputed: a stream asking for a dictionary of its own choosing.
func withDictionary(t *testing.T, stream []byte, props byte) []byte {
t.Helper()
out := append([]byte{}, stream...)
const at = 12 // the stream header
size := (int(out[at]) + 1) * 4
header := out[at : at+size]
flags := header[1]
i := 2
readVarint := func() {
for header[i]&0x80 != 0 {
i++
}
i++
}
if flags&0x40 != 0 {
readVarint()
}
if flags&0x80 != 0 {
readVarint()
}
if header[i] != 0x21 || header[i+1] != 0x01 {
t.Fatalf("the fixture's first filter is not LZMA2: % x", header)
}
header[i+2] = props
binary.LittleEndian.PutUint32(header[size-4:], crc32.ChecksumIEEE(header[:size-4]))
return out
}
func TestAnXzStreamAskingForTooLargeADictionaryIsRefused(t *testing.T) {
body, err := os.ReadFile(filepath.Join("testdata", "release.tar.xz"))
if err != nil {
t.Fatal(err)
}
// 1 GiB (props 36) and 4 GiB less one (props 40): over the 64 MiB maximum, refused before allocation.
for _, props := range []byte{36, 40} {
crafted := withDictionary(t, body, props)
v := &vendor{files: map[string][]byte{"/r.tar.xz": crafted}}
base := serveVendor(t, v)
var before, after runtime.MemStats
runtime.GC()
runtime.ReadMemStats(&before)
_, err := upstreamArchive(context.Background(), t.TempDir(), catalogue.Artifact{Name: "archive", Kind: "archive",
URL: base + "/r.tar.xz", SHA256: sumHex(crafted), Version: "1", Within: "betterbird"}, func(string, string, ...any) {})
runtime.ReadMemStats(&after)
if err == nil || !strings.Contains(err.Error(), "dictionary") {
t.Errorf("props %d: a dictionary over the maximum was not refused: %v", props, err)
}
if grew := after.TotalAlloc - before.TotalAlloc; grew > 256<<20 {
t.Errorf("props %d: %d bytes were allocated for a refused stream", props, grew)
}
}
// The fixture as it is (a small dictionary) still reads.
if crafted := withDictionary(t, body, 0); len(crafted) == 0 {
t.Fatal("no fixture")
}
}