From 67eca8a21251c5b30bb8fb744ce8ecb464f3c87f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Tue, 24 Mar 2026 01:57:02 +0100 Subject: [PATCH] ext4: return error instead of panicking on special file inodes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpenFile and openFileViaInode panic with a nil pointer dereference when called on character device, block device, FIFO, or socket inodes. These inode types store device major/minor numbers instead of an extent tree, so inode.extents is nil and the call to inode.extents.blocks() crashes. Add a nil guard that returns a descriptive error before accessing the extent tree. Signed-off-by: Paweł Gronowski --- filesystem/ext4/ext4.go | 6 ++++++ filesystem/ext4/specialfile_test.go | 31 ++++++++++++++++++++++++++++ filesystem/ext4/testdata/buildimg.sh | 2 ++ 3 files changed, 39 insertions(+) create mode 100644 filesystem/ext4/specialfile_test.go diff --git a/filesystem/ext4/ext4.go b/filesystem/ext4/ext4.go index 1224560..1831880 100644 --- a/filesystem/ext4/ext4.go +++ b/filesystem/ext4/ext4.go @@ -1272,6 +1272,9 @@ func (fs *FileSystem) OpenFile(p string, flag int) (filesystem.File, error) { } return fs.OpenFile(linkTarget, flag) } + if inode.extents == nil { + return nil, fmt.Errorf("cannot open special file %s (inode %d): no extent tree", p, inodeNumber) + } offset := int64(0) if flag&os.O_APPEND == os.O_APPEND { offset = int64(inode.size) @@ -1306,6 +1309,9 @@ func (fs *FileSystem) openFileViaInode(inodeNumber uint32, flag int) (filesystem if inode.fileType == fileTypeSymbolicLink { return nil, fmt.Errorf("cannot open file via inode: inode %d is a symbolic link", inodeNumber) } + if inode.extents == nil { + return nil, fmt.Errorf("cannot open special file (inode %d): no extent tree", inodeNumber) + } offset := int64(0) if flag&os.O_APPEND == os.O_APPEND { offset = int64(inode.size) diff --git a/filesystem/ext4/specialfile_test.go b/filesystem/ext4/specialfile_test.go new file mode 100644 index 0000000..00c5299 --- /dev/null +++ b/filesystem/ext4/specialfile_test.go @@ -0,0 +1,31 @@ +package ext4 + +import ( + "os" + "strings" + "testing" + + "github.com/diskfs/go-diskfs/backend/file" +) + +func TestOpenFileSpecialFileReturnsError(t *testing.T) { + f, err := os.Open(imgFile) + if err != nil { + t.Fatalf("Error opening test image: %v", err) + } + defer f.Close() + + b := file.New(f, true) + fs, err := Read(b, 100*MB, 0, 512) + if err != nil { + t.Fatalf("Error reading filesystem: %v", err) + } + + _, err = fs.OpenFile("/chardev", os.O_RDONLY) + if err == nil { + t.Fatal("OpenFile on character device should return an error, not succeed") + } + if !strings.Contains(err.Error(), "cannot open special file") { + t.Errorf("expected 'cannot open special file' error, got: %v", err) + } +} diff --git a/filesystem/ext4/testdata/buildimg.sh b/filesystem/ext4/testdata/buildimg.sh index a399b3f..ddb272c 100755 --- a/filesystem/ext4/testdata/buildimg.sh +++ b/filesystem/ext4/testdata/buildimg.sh @@ -31,6 +31,8 @@ ln -s nonexistent deadlink ln -s /some/really/long/path/that/does/not/exist/and/does/not/fit/in/symlink deadlonglink # the target here is >60 chars and so will not fit within the inode # hardlink ln random.dat hardlink.dat +# character device for special file tests +mknod chardev c 1 3 cd /data umount /mnt