Close diagnostic and restore coverage gaps

This commit is contained in:
2026-08-11 12:28:55 +00:00
parent 8ef6e99d69
commit a2a144dffa
4 changed files with 289 additions and 12 deletions

View File

@@ -156,13 +156,17 @@ func setOpenedDirectoryMode(root *os.Root, mode os.FileMode) error {
}
func createSiblingTemp(parent *os.Root, base string) (*os.File, string, error) {
return createSiblingTempWithFlags(parent, base, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o600)
}
func createSiblingTempWithFlags(parent *os.Root, base string, flags int, mode os.FileMode) (*os.File, string, error) {
for attempt := 0; attempt < 100; attempt++ {
var randomBytes [16]byte
if _, err := rand.Read(randomBytes[:]); err != nil {
return nil, "", fmt.Errorf("generate temporary file name: %w", err)
}
name := "." + base + ".tmp-" + hex.EncodeToString(randomBytes[:])
file, err := parent.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o600)
file, err := parent.OpenFile(name, flags|os.O_CREATE|os.O_EXCL, mode)
if errors.Is(err, os.ErrExist) {
continue
}
@@ -184,28 +188,128 @@ func syncOpenedDirectory(parent *os.Root) error {
}
// OpenFileConfined opens a file after verifying its parent hierarchy without
// following symbolic links. Existing symbolic-link and non-regular leaves are
// mutating through symbolic links. Truncating opens atomically replace the leaf
// with a newly created sibling, while other creating opens never carry O_CREATE
// into an existing-leaf open. Existing symbolic-link and non-regular leaves are
// rejected.
func OpenFileConfined(path string, flags int, mode os.FileMode) (*os.File, error) {
return openFileConfined(path, flags, mode, nil)
}
func openFileConfined(path string, flags int, mode os.FileMode, beforeOpen func() error) (*os.File, error) {
parent, name, err := openConfinedParent(path, false, 0)
if err != nil {
return nil, err
}
defer func() { _ = parent.Close() }()
if info, err := parent.Lstat(name); err == nil {
if info.Mode()&os.ModeSymlink != 0 {
return nil, fmt.Errorf("destination file %q is a symbolic link", name)
if flags&(os.O_CREATE|os.O_EXCL) == os.O_CREATE|os.O_EXCL {
if err := runBeforeConfinedOpen(beforeOpen); err != nil {
return nil, err
}
if !info.Mode().IsRegular() {
return nil, fmt.Errorf("destination file %q is not a regular file", name)
file, err := parent.OpenFile(name, flags, mode)
if err != nil {
return nil, err
}
} else if err != nil && !errors.Is(err, os.ErrNotExist) {
return nil, fmt.Errorf("inspect destination file %q: %w", name, err)
return validateOpenedConfinedFile(parent, name, file)
}
file, err := parent.OpenFile(name, flags, mode)
exists, err := validateConfinedLeaf(parent, name)
if err != nil {
return nil, err
}
if flags&os.O_TRUNC != 0 {
if !exists && flags&os.O_CREATE == 0 {
return nil, fmt.Errorf("open destination file %q: %w", name, os.ErrNotExist)
}
return replaceAndOpenConfinedFile(parent, name, flags, mode, beforeOpen)
}
if err := runBeforeConfinedOpen(beforeOpen); err != nil {
return nil, err
}
if !exists && flags&os.O_CREATE != 0 {
file, err := parent.OpenFile(name, flags|os.O_EXCL, mode)
if err == nil {
return validateOpenedConfinedFile(parent, name, file)
}
if !errors.Is(err, os.ErrExist) {
return nil, err
}
if _, validationErr := validateConfinedLeaf(parent, name); validationErr != nil {
return nil, validationErr
}
}
// The leaf now exists, so removing O_CREATE prevents a raced dangling
// symlink from creating its target. Opening an existing symlink can expose a
// handle, but no caller can mutate through it before the identity check below.
file, err := parent.OpenFile(name, flags&^os.O_CREATE, mode)
if err != nil {
return nil, err
}
return validateOpenedConfinedFile(parent, name, file)
}
func validateConfinedLeaf(parent *os.Root, name string) (bool, error) {
info, err := parent.Lstat(name)
if errors.Is(err, os.ErrNotExist) {
return false, nil
}
if err != nil {
return false, fmt.Errorf("inspect destination file %q: %w", name, err)
}
if info.Mode()&os.ModeSymlink != 0 {
return false, fmt.Errorf("destination file %q is a symbolic link", name)
}
if !info.Mode().IsRegular() {
return false, fmt.Errorf("destination file %q is not a regular file", name)
}
return true, nil
}
func replaceAndOpenConfinedFile(parent *os.Root, name string, flags int, mode os.FileMode, beforeOpen func() error) (_ *os.File, resultErr error) {
temporary, temporaryName, err := createSiblingTempWithFlags(parent, name, flags&^os.O_TRUNC, mode)
if err != nil {
return nil, fmt.Errorf("create replacement for destination file %q: %w", name, err)
}
installed := false
defer func() {
if installed {
return
}
closeErr := temporary.Close()
removeErr := parent.Remove(temporaryName)
if errors.Is(removeErr, os.ErrNotExist) {
removeErr = nil
}
resultErr = errors.Join(resultErr, closeErr, removeErr)
}()
if err := runBeforeConfinedOpen(beforeOpen); err != nil {
return nil, err
}
// Rename replaces the directory entry itself rather than following it. A
// leaf changed to a symlink after validation therefore cannot redirect the
// truncation to its target.
if err := parent.Rename(temporaryName, name); err != nil {
return nil, fmt.Errorf("install replacement for destination file %q: %w", name, err)
}
installed = true
return validateOpenedConfinedFile(parent, name, temporary)
}
func runBeforeConfinedOpen(beforeOpen func() error) error {
if beforeOpen == nil {
return nil
}
if err := beforeOpen(); err != nil {
return fmt.Errorf("check before confined file open: %w", err)
}
return nil
}
func validateOpenedConfinedFile(parent *os.Root, name string, file *os.File) (*os.File, error) {
opened, err := file.Stat()
if err != nil {
_ = file.Close()