Skip to content

Use syscall.Dup or such in NewDiskBlockDeviceStorageDeviceAttachment? #201

@cfergeau

Description

@cfergeau

Describe the bug

This came up in crc-org/vfkit#373 (comment)
The code roughly does:

		f, err := os.OpenFile(devPath, open_flags, 0)
		if err != nil {
			return nil, fmt.Errorf("error opening file: %v", err)
		}

		attachment, err := vz.NewDiskBlockDeviceStorageDeviceAttachment(f, conf.ReadOnly, vz.DiskSynchronizationModeFull)
		if err != nil {
			_ = f.Close()
			return nil, fmt.Errorf("error creating disk attachment: %v", err)
		}
		// create device with attachment and add it to VM

After this function returns, f can be garbage collected at any time, which will close its file descriptor.
The relevant code in Code-Hex/vz is https://github.com/Code-Hex/vz/blob/c17cd79c8418115e1fcbf98c78f53bc62fd0c972/storage.go#L384C6-L408 , it uses file.Fd() directly, and then it’s used as

vz/virtualization_14.m

Lines 40 to 53 in c17cd79

void *newVZDiskBlockDeviceStorageDeviceAttachment(int fileDescriptor, bool readOnly, int syncMode, void **error)
{
#ifdef INCLUDE_TARGET_OSX_14
if (@available(macOS 14, *)) {
NSFileHandle *fileHandle = [[NSFileHandle alloc] initWithFileDescriptor:fileDescriptor];
return [[VZDiskBlockDeviceStorageDeviceAttachment alloc]
initWithFileHandle:fileHandle
readOnly:(BOOL)readOnly
synchronizationMode:(VZDiskSynchronizationMode)syncMode
error:(NSError *_Nullable *_Nullable)error];
}
#endif
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
}

I don’t think NSFileHandle *fileHandle = [[NSFileHandle alloc] initWithFileDescriptor:fileDescriptor]; will dup() the file descriptor.

The end result is that we’ll be getting errors such as this when f gets closed prematurely:

NSLocalizedFailure = "Invalid virtual machine configuration.";
NSLocalizedFailureReason = "The storage device attachment is invalid."

This is a bit misleading as the doc says The file handle is retained by the disk attachment.

To Reproduce
I don’t have an easy reproducer, but storage_test.go can probably trigger this in a new test case for block devices and artificial calls to runtime.GC().
The vfkit reproducer is described at crc-org/vfkit#373 (comment) but has been fixed in the latest iteration (crc-org/vfkit#373 (comment))

Expected behavior
Either NewDiskBlockDeviceStorageDeviceAttachment() works fine even if the *os.File argument is closed after it completes, or its documentation changed to explicitly say that *os.File must not be closed/garbage collected while the attachment is in use.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions