Skip to content

Commit

Permalink
pkg/archive: avoid user lookups when generating tarball
Browse files Browse the repository at this point in the history
This change introduces a new approach to generating tar headers in a way
that avoids system-dependent lookups and potential calls to glibc, enhancing
portability and security.

The same logic is used by Moby, and the override function is based on
code from Moby.

Closes: containers#1836

Signed-off-by: Giuseppe Scrivano <[email protected]>
  • Loading branch information
giuseppe committed Feb 19, 2024
1 parent 91725e0 commit e0bd86a
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 1 deletion.
33 changes: 32 additions & 1 deletion pkg/archive/archive.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,12 +339,43 @@ func (compression *Compression) Extension() string {
return ""
}

// nosysFileInfo hides the system-dependent info of the wrapped FileInfo to
// prevent tar.FileInfoHeader from introspecting it and potentially calling into
// glibc.
type nosysFileInfo struct {
os.FileInfo
}

func (fi nosysFileInfo) Sys() interface{} {
// A Sys value of type *tar.Header is safe as it is system-independent.
// The tar.FileInfoHeader function copies the fields into the returned
// header without performing any OS lookups.
if sys, ok := fi.FileInfo.Sys().(*tar.Header); ok {
return sys
}
return nil
}

// sysStatOverride, if non-nil, populates hdr from system-dependent fields of fi.
var sysStatOverride func(fi os.FileInfo, hdr *tar.Header) error

func FileInfoHeaderNoLookups(fi os.FileInfo, link string) (*tar.Header, error) {
if sysStatOverride == nil {
return tar.FileInfoHeader(fi, link)
}
hdr, err := tar.FileInfoHeader(nosysFileInfo{fi}, link)
if err != nil {
return nil, err
}
return hdr, sysStatOverride(fi, hdr)
}

// FileInfoHeader creates a populated Header from fi.
// Compared to archive pkg this function fills in more information.
// Also, regardless of Go version, this function fills file type bits (e.g. hdr.Mode |= modeISDIR),
// which have been deleted since Go 1.9 archive/tar.
func FileInfoHeader(name string, fi os.FileInfo, link string) (*tar.Header, error) {
hdr, err := tar.FileInfoHeader(fi, link)
hdr, err := FileInfoHeaderNoLookups(fi, link)
if err != nil {
return nil, err
}
Expand Down
25 changes: 25 additions & 0 deletions pkg/archive/archive_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,31 @@ import (
"golang.org/x/sys/unix"
)

func init() {
sysStatOverride = statUnix
}

// statUnix populates hdr from system-dependent fields of fi without performing
// any OS lookups.
// Adapted from Moby.
func statUnix(fi os.FileInfo, hdr *tar.Header) error {
s, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
return nil
}

hdr.Uid = int(s.Uid)
hdr.Gid = int(s.Gid)

if s.Mode&unix.S_IFBLK != 0 ||
s.Mode&unix.S_IFCHR != 0 {
hdr.Devmajor = int64(unix.Major(uint64(s.Rdev))) //nolint: unconvert
hdr.Devminor = int64(unix.Minor(uint64(s.Rdev))) //nolint: unconvert
}

return nil
}

// fixVolumePathPrefix does platform specific processing to ensure that if
// the path being passed in is not in a volume path format, convert it to one.
func fixVolumePathPrefix(srcPath string) string {
Expand Down

0 comments on commit e0bd86a

Please sign in to comment.