diff --git a/pkg/archive/tar_unix.go b/pkg/archive/tar_unix.go index 684ea5783d..59ee39ecb1 100644 --- a/pkg/archive/tar_unix.go +++ b/pkg/archive/tar_unix.go @@ -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))) } diff --git a/pkg/archive/tar_unix_test.go b/pkg/archive/tar_unix_test.go new file mode 100644 index 0000000000..682543cd5f --- /dev/null +++ b/pkg/archive/tar_unix_test.go @@ -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) + } + }) + } +}