pkg/archive: reject out-of-range device numbers in layer headers

Signed-off-by: Aysha Afrah Ziya <aysha26@digiscrypt.com>
This commit is contained in:
Aysha Afrah Ziya
2026-07-14 20:10:05 +05:30
parent 70ec5b89f4
commit 0205398ac2
2 changed files with 63 additions and 0 deletions

View File

@@ -22,6 +22,7 @@ import (
"archive/tar"
"errors"
"fmt"
"math"
"os"
"runtime"
"strings"
@@ -121,6 +122,16 @@ func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error {
mode |= unix.S_IFIFO
}
// Devmajor and Devminor come straight from the (untrusted) tar header as
// int64, but Mkdev only takes uint32. Casting a value that does not fit
// silently truncates it, so the node created on disk would carry a
// different major/minor than the header declares. Reject those instead of
// creating a mismatched device.
if hdr.Devmajor < 0 || hdr.Devmajor > math.MaxUint32 ||
hdr.Devminor < 0 || hdr.Devminor > math.MaxUint32 {
return fmt.Errorf("device number %d:%d for %q out of range: %w", hdr.Devmajor, hdr.Devminor, hdr.Name, errInvalidArchive)
}
return mknod(path, mode, unix.Mkdev(uint32(hdr.Devmajor), uint32(hdr.Devminor)))
}

View File

@@ -0,0 +1,52 @@
//go:build !windows
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package archive
import (
"archive/tar"
"errors"
"math"
"path/filepath"
"testing"
)
func TestHandleTarTypeBlockCharFifoDeviceRange(t *testing.T) {
for _, tc := range []struct {
name string
devmajor int64
devminor int64
}{
{"major above uint32", math.MaxUint32 + 1, 0},
{"minor above uint32", 0, math.MaxUint32 + 1},
{"negative major", -1, 0},
{"negative minor", 0, -1},
} {
t.Run(tc.name, func(t *testing.T) {
hdr := &tar.Header{
Typeflag: tar.TypeBlock,
Devmajor: tc.devmajor,
Devminor: tc.devminor,
}
err := handleTarTypeBlockCharFifo(hdr, filepath.Join(t.TempDir(), "dev"))
if !errors.Is(err, errInvalidArchive) {
t.Fatalf("expected errInvalidArchive for %d:%d, got %v", tc.devmajor, tc.devminor, err)
}
})
}
}