mirror of
https://github.com/containers/podman.git
synced 2025-10-12 00:35:05 +08:00
Merge pull request #26969 from mheon/backports_561
Backports for v5.6.1
This commit is contained in:
@ -1,5 +1,12 @@
|
||||
# Release Notes
|
||||
|
||||
## 5.6.1
|
||||
### Bugfixes
|
||||
- Fixed a bug where network creation and removal events were displayed incorrectly when the `journald` events driver was in use.
|
||||
- Fixed a bug where the `--security-opt seccomp=unconfined` option was broken on Windows ([#26855](https://github.com/containers/podman/issues/26855)).
|
||||
- Fixed a bug where containers created with a name longer than 64 characters, no explicit hostname, the the `container_name_as_hostname` option in `containers.conf` set to `true` would fail to start.
|
||||
- Fixed a bug where Podman would fail to start containers when runc 1.3.0 or later was used as the OCI runtime ([#26938](https://github.com/containers/podman/issues/26938)).
|
||||
|
||||
## 5.6.0
|
||||
### Features
|
||||
- A new set of commands for managing Quadlets has been added as `podman quadlet install` (install a new Quadlet for the current user), `podman quadlet list` (list installed Quadlets), `podman quadlet print` (print the contents of a Quadlet file), and `podman quadlet rm` (remove a Quadlet). These commands are presently not available with the remote Podman client - we expect support for this to arrive in a future release.
|
||||
|
2
go.mod
2
go.mod
@ -170,7 +170,7 @@ require (
|
||||
github.com/tklauser/go-sysconf v0.3.14 // indirect
|
||||
github.com/tklauser/numcpus v0.9.0 // indirect
|
||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
|
||||
github.com/ulikunitz/xz v0.5.12 // indirect
|
||||
github.com/ulikunitz/xz v0.5.15 // indirect
|
||||
github.com/vbatts/tar-split v0.12.1 // indirect
|
||||
github.com/vishvananda/netns v0.0.5 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
|
4
go.sum
4
go.sum
@ -434,8 +434,8 @@ github.com/u-root/u-root v0.12.1-0.20240114161452-ab3534910ced h1:G0F7Hmwph1Ojoz
|
||||
github.com/u-root/u-root v0.12.1-0.20240114161452-ab3534910ced/go.mod h1:jtkuv6BVn5jo/WAHgQ1k9XfzHEe1hZmq9yDUvbgL+Iw=
|
||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM=
|
||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
|
||||
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
|
||||
github.com/ulikunitz/xz v0.5.12/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=
|
||||
|
@ -741,14 +741,14 @@ func (c *Container) hostname(network bool) string {
|
||||
// containers.conf, use a sanitized version of the container's name
|
||||
// as the hostname. Since the container name must already match
|
||||
// the set '[a-zA-Z0-9][a-zA-Z0-9_.-]*', we can just remove any
|
||||
// underscores and limit it to 253 characters to make it a valid
|
||||
// underscores and limit it to 64 characters to make it a valid
|
||||
// hostname.
|
||||
if c.runtime.config.Containers.ContainerNameAsHostName {
|
||||
sanitizedHostname := strings.ReplaceAll(c.Name(), "_", "")
|
||||
if len(sanitizedHostname) <= 253 {
|
||||
if len(sanitizedHostname) <= 64 {
|
||||
return sanitizedHostname
|
||||
}
|
||||
return sanitizedHostname[:253]
|
||||
return sanitizedHostname[:64]
|
||||
}
|
||||
|
||||
// Otherwise use the container's short ID as the hostname.
|
||||
|
@ -420,6 +420,8 @@ func (c *Container) generateSpec(ctx context.Context) (s *spec.Spec, cleanupFunc
|
||||
// Podman decided for --no-dereference as many
|
||||
// bin-utils tools (e..g, touch, chown, cp) do.
|
||||
options = append(options, "copy-symlink")
|
||||
case "copy", "nocopy":
|
||||
// no real OCI runtime bind mount options, these should already be handled by the named volume mount above
|
||||
default:
|
||||
options = append(options, o)
|
||||
}
|
||||
|
@ -504,6 +504,15 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
|
||||
_, err := r.state.Volume(vol.Name)
|
||||
if err == nil {
|
||||
// The volume exists, we're good
|
||||
// Make sure to drop all volume-opt options as they only apply to
|
||||
// the volume create which we don't do again.
|
||||
var volOpts []string
|
||||
for _, opts := range vol.Options {
|
||||
if !strings.HasPrefix(opts, "volume-opt") {
|
||||
volOpts = append(volOpts, opts)
|
||||
}
|
||||
}
|
||||
vol.Options = volOpts
|
||||
continue
|
||||
} else if !errors.Is(err, define.ErrNoSuchVolume) {
|
||||
return nil, fmt.Errorf("retrieving named volume %s for new container: %w", vol.Name, err)
|
||||
@ -530,6 +539,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
|
||||
if len(vol.Options) > 0 {
|
||||
isDriverOpts := false
|
||||
driverOpts := make(map[string]string)
|
||||
var volOpts []string
|
||||
for _, opts := range vol.Options {
|
||||
if strings.HasPrefix(opts, "volume-opt") {
|
||||
isDriverOpts = true
|
||||
@ -538,8 +548,11 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
|
||||
return nil, err
|
||||
}
|
||||
driverOpts[driverOptKey] = driverOptValue
|
||||
} else {
|
||||
volOpts = append(volOpts, opts)
|
||||
}
|
||||
}
|
||||
vol.Options = volOpts
|
||||
if isDriverOpts {
|
||||
parsedOptions := []VolumeCreateOption{WithVolumeOptions(driverOpts)}
|
||||
volOptions = append(volOptions, parsedOptions...)
|
||||
|
@ -788,7 +788,7 @@ var _ = Describe("Verify podman containers.conf usage", func() {
|
||||
})
|
||||
|
||||
startContainer := func(params ...string) string {
|
||||
args := []string{"create"}
|
||||
args := []string{"run", "-d"}
|
||||
for _, param := range params {
|
||||
if param == "--name" {
|
||||
args = append(args, "--replace")
|
||||
@ -888,7 +888,7 @@ var _ = Describe("Verify podman containers.conf usage", func() {
|
||||
name = getContainerConfig(containerID, "{{ .Name }}")
|
||||
// Double check that name actually got set correctly
|
||||
Expect(name).To(Equal(longHostname))
|
||||
// Hostname should be the container name truncated to 253 characters
|
||||
Expect(hostname).To(Equal(name[:253]))
|
||||
// Hostname should be the container name truncated to 64 characters
|
||||
Expect(hostname).To(Equal(name[:64]))
|
||||
})
|
||||
})
|
||||
|
@ -3,17 +3,20 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
. "github.com/containers/podman/v5/test/utils"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
. "github.com/onsi/gomega/gexec"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
// in-container mount point: using a path that is definitely not present
|
||||
@ -447,9 +450,27 @@ var _ = Describe("Podman run with volumes", func() {
|
||||
Expect(separateVolumeSession).Should(ExitCleanly())
|
||||
Expect(separateVolumeSession.OutputToString()).To(Equal(baselineOutput))
|
||||
|
||||
copySession := podmanTest.Podman([]string{"run", "--rm", "-v", "testvol3:/etc/apk:copy", ALPINE, "stat", "-c", "%h", "/etc/apk/arch"})
|
||||
copySession.WaitWithDefaultTimeout()
|
||||
Expect(copySession).Should(ExitCleanly())
|
||||
podmanTest.PodmanExitCleanly("run", "--name", "testctr", "-v", "testvol3:/etc/apk:copy", ALPINE, "stat", "-c", "%h", "/etc/apk/arch")
|
||||
|
||||
inspect := podmanTest.PodmanExitCleanly("container", "inspect", "testctr", "--format", "{{.OCIConfigPath}}")
|
||||
|
||||
// Make extra check that the OCI config does not contain the copy opt, runc 1.3.0 fails on that while crun does not.
|
||||
// We only test crun upstream so make sure the spec is sane: https://github.com/containers/podman/issues/26938
|
||||
f, err := os.Open(inspect.OutputToString())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer f.Close()
|
||||
var spec specs.Spec
|
||||
err = json.NewDecoder(f).Decode(&spec)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
found := false
|
||||
for _, m := range spec.Mounts {
|
||||
if m.Destination == "/etc/apk" {
|
||||
found = true
|
||||
Expect(m.Options).To(Equal([]string{"rprivate", "nosuid", "nodev", "rbind"}))
|
||||
}
|
||||
}
|
||||
Expect(found).To(BeTrue(), "OCI spec contains /etc/apk mount")
|
||||
|
||||
noCopySession := podmanTest.Podman([]string{"run", "--rm", "-v", "testvol4:/etc/apk:nocopy", ALPINE, "stat", "-c", "%h", "/etc/apk/arch"})
|
||||
noCopySession.WaitWithDefaultTimeout()
|
||||
@ -859,14 +880,17 @@ VOLUME /test/`, ALPINE)
|
||||
It("podman run with --mount and named volume with driver-opts", func() {
|
||||
// anonymous volume mount with driver opts
|
||||
vol := "type=volume,source=test_vol,dst=/test,volume-opt=type=tmpfs,volume-opt=device=tmpfs,volume-opt=o=nodev"
|
||||
session := podmanTest.Podman([]string{"run", "--rm", "--mount", vol, ALPINE, "echo", "hello"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(ExitCleanly())
|
||||
// Loop twice to cover both the initial code path that creates the volume and the ones which reuses it.
|
||||
for i := range 2 {
|
||||
name := "testctr" + strconv.Itoa(i)
|
||||
podmanTest.PodmanExitCleanly("run", "--name", name, "--mount", vol, ALPINE, "echo", "hello")
|
||||
|
||||
inspectVol := podmanTest.Podman([]string{"volume", "inspect", "test_vol"})
|
||||
inspectVol.WaitWithDefaultTimeout()
|
||||
Expect(inspectVol).Should(ExitCleanly())
|
||||
Expect(inspectVol.OutputToString()).To(ContainSubstring("nodev"))
|
||||
inspectVol := podmanTest.PodmanExitCleanly("volume", "inspect", "test_vol")
|
||||
Expect(inspectVol.OutputToString()).To(ContainSubstring("nodev"))
|
||||
|
||||
inspect := podmanTest.PodmanExitCleanly("container", "inspect", name, "--format", "{{range .Mounts}}{{.Options}}{{end}}")
|
||||
Expect(inspect.OutputToString()).To(ContainSubstring("[nosuid nodev rbind]"))
|
||||
}
|
||||
})
|
||||
|
||||
It("volume permissions after run", func() {
|
||||
|
@ -338,7 +338,7 @@ EOF
|
||||
# send a random string to the container. This will cause the container
|
||||
# to output the string to its logs, then exit.
|
||||
teststring=$(random_string 30)
|
||||
echo "$teststring" | nc 127.0.0.1 $port_out
|
||||
echo "$teststring" > /dev/tcp/127.0.0.1/$port_out
|
||||
|
||||
# Confirm that the container log output is the string we sent it.
|
||||
run_podman wait $cid
|
||||
|
@ -1188,8 +1188,8 @@ spec:
|
||||
EOF
|
||||
|
||||
# Bind the port to force a an error when starting the pod
|
||||
timeout --foreground -v --kill=10 10 ncat -l 127.0.0.1 $port &
|
||||
nc_pid=$!
|
||||
timeout --foreground -v --kill=10 10 socat TCP-LISTEN:$port,bind=127.0.0.1,fork - &
|
||||
socat_pid=$!
|
||||
|
||||
# Create the Quadlet file
|
||||
local quadlet_file=$PODMAN_TMPDIR/start_err_$(safename).kube
|
||||
@ -1214,7 +1214,7 @@ EOF
|
||||
run -0 journalctl -eu $QUADLET_SERVICE_NAME
|
||||
assert "$output" =~ "$port: bind: address already in use" "journal contains the real podman start error"
|
||||
|
||||
kill "$nc_pid"
|
||||
kill "$socat_pid"
|
||||
}
|
||||
|
||||
# https://github.com/containers/podman/issues/25786
|
||||
|
@ -153,7 +153,7 @@ load helpers.network
|
||||
|
||||
# emit random string, and check it
|
||||
teststring=$(random_string 30)
|
||||
echo "$teststring" | nc 127.0.0.1 $myport
|
||||
echo "$teststring" > /dev/tcp/127.0.0.1/$myport
|
||||
|
||||
run_podman logs $cid
|
||||
# Sigh. We can't check line-by-line, because 'nc' output order is
|
||||
@ -296,7 +296,7 @@ load helpers.network
|
||||
|
||||
# emit random string, and check it
|
||||
teststring=$(random_string 30)
|
||||
echo "$teststring" | nc 127.0.0.1 $myport
|
||||
echo "$teststring" > /dev/tcp/127.0.0.1/$myport
|
||||
|
||||
run_podman logs $cid
|
||||
# Sigh. We can't check line-by-line, because 'nc' output order is
|
||||
@ -806,26 +806,26 @@ nameserver 8.8.8.8" "nameserver order is correct"
|
||||
cid="$output"
|
||||
|
||||
# make sure binding the same port fails
|
||||
run timeout 5 ncat -l 127.0.0.1 $port
|
||||
assert "$status" -eq 2 "ncat unexpected exit code"
|
||||
assert "$output" =~ "127.0.0.1:$port: Address already in use" "ncat error message"
|
||||
run timeout 5 socat TCP-LISTEN:$port,bind=127.0.0.1,fork -
|
||||
assert "$status" -eq 1 "socat unexpected exit code"
|
||||
assert "$output" =~ ".* 127.0.0.1:$port.* Address already in use" "socat error message"
|
||||
|
||||
for port in $(seq $port $end_port); do
|
||||
run_podman exec -d $cid nc -l -p $port -e /bin/cat
|
||||
|
||||
# we have to rety ncat as it can flake as we exec in the background so nc -l
|
||||
# we have to retry socat as it can flake as we exec in the background so nc -l
|
||||
# might not have bound the port yet, retry seems simpler than checking if the
|
||||
# port is bound in the container, https://github.com/containers/podman/issues/21561.
|
||||
retries=5
|
||||
while [[ $retries -gt 0 ]]; do
|
||||
run ncat 127.0.0.1 $port <<<$random
|
||||
run socat - TCP:127.0.0.1:$port <<<$random
|
||||
if [[ $status -eq 0 ]]; then
|
||||
break
|
||||
fi
|
||||
sleep 0.5
|
||||
retries=$((retries -1))
|
||||
done
|
||||
is "$output" "$random" "ncat got data back (netmode=$netmode port=$port)"
|
||||
is "$output" "$random" "socat got data back (netmode=$netmode port=$port)"
|
||||
done
|
||||
|
||||
run_podman rm -f -t0 $cid
|
||||
|
@ -86,7 +86,6 @@ Requirements
|
||||
- bats
|
||||
- jq
|
||||
- skopeo
|
||||
- nmap-ncat
|
||||
- httpd-tools
|
||||
- openssl
|
||||
- socat
|
||||
|
@ -423,7 +423,7 @@ function wait_for_port() {
|
||||
function tcp_port_probe() {
|
||||
local address="${2:-0.0.0.0}"
|
||||
|
||||
: | nc "${address}" "${1}"
|
||||
(exec echo -n >/dev/tcp/"$address/$1") >/dev/null 2>&1
|
||||
}
|
||||
|
||||
### Pasta Helpers ##############################################################
|
||||
|
17
vendor/github.com/ulikunitz/xz/TODO.md
generated
vendored
17
vendor/github.com/ulikunitz/xz/TODO.md
generated
vendored
@ -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,19 @@
|
||||
|
||||
## 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
|
||||
together. So I added a new package xio with a WriteCloserStack to address the
|
||||
issue.
|
||||
|
||||
### 2024-04-03
|
||||
|
||||
Release v0.5.12 updates README.md and SECURITY.md to address the supply chain
|
||||
|
55
vendor/github.com/ulikunitz/xz/lzma/header.go
generated
vendored
55
vendor/github.com/ulikunitz/xz/lzma/header.go
generated
vendored
@ -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
|
||||
}
|
||||
|
123
vendor/github.com/ulikunitz/xz/lzma/reader.go
generated
vendored
123
vendor/github.com/ulikunitz/xz/lzma/reader.go
generated
vendored
@ -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
|
||||
|
28
vendor/github.com/ulikunitz/xz/lzma/writer.go
generated
vendored
28
vendor/github.com/ulikunitz/xz/lzma/writer.go
generated
vendored
@ -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
2
vendor/modules.txt
vendored
@ -899,7 +899,7 @@ github.com/tklauser/numcpus
|
||||
# github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701
|
||||
## explicit; go 1.21
|
||||
github.com/u-root/uio/ulog
|
||||
# github.com/ulikunitz/xz v0.5.12
|
||||
# github.com/ulikunitz/xz v0.5.15
|
||||
## explicit; go 1.12
|
||||
github.com/ulikunitz/xz
|
||||
github.com/ulikunitz/xz/internal/hash
|
||||
|
Reference in New Issue
Block a user