aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2022-09-15 13:36:15 +0000
committerAleksandr Nogikh <wp32pw@gmail.com>2022-09-27 13:07:37 +0200
commit2b420c9686b7c95216d491a97fcd5158acb810f2 (patch)
tree091b25ff54bf7f41a7fcf3a027a714fb2e7255b8
parent1856cdc9b3652a082c5bfa0e08a9f883baece8ec (diff)
sys/linux: extract raw images from syz_mount_image
To simplify the extraction code, let's make segments non-overlapping even before execution.
-rw-r--r--dashboard/dashapi/dashapi.go1
-rw-r--r--prog/analysis.go38
-rw-r--r--prog/target.go6
-rw-r--r--sys/linux/init.go1
-rw-r--r--sys/linux/init_images.go95
-rw-r--r--sys/linux/init_images_test.go126
-rw-r--r--sys/linux/testdata/fs_images/0.in1
-rw-r--r--sys/linux/testdata/fs_images/0.out0bin0 -> 8736 bytes
-rw-r--r--sys/linux/testdata/fs_images/1.in1
-rw-r--r--sys/linux/testdata/fs_images/1.out0bin0 -> 262144 bytes
10 files changed, 251 insertions, 18 deletions
diff --git a/dashboard/dashapi/dashapi.go b/dashboard/dashapi/dashapi.go
index 2a6c8df22..cbc89cce2 100644
--- a/dashboard/dashapi/dashapi.go
+++ b/dashboard/dashapi/dashapi.go
@@ -417,6 +417,7 @@ const (
KernelObject AssetType = "kernel_object"
KernelImage AssetType = "kernel_image"
HTMLCoverageReport AssetType = "html_coverage_report"
+ MountInRepro AssetType = "mount_in_repro"
)
type BisectResult struct {
diff --git a/prog/analysis.go b/prog/analysis.go
index 1b572f0c0..04f0b270a 100644
--- a/prog/analysis.go
+++ b/prog/analysis.go
@@ -10,6 +10,7 @@ package prog
import (
"fmt"
+ "io"
)
type state struct {
@@ -337,3 +338,40 @@ func checkMaxCallID(id int) {
panic(fmt.Sprintf("too many syscalls, have %v, max supported %v", id, fallbackCallMask+1))
}
}
+
+type ExtractedAssetType int
+
+const (
+ MountInRepro ExtractedAssetType = iota
+)
+
+type ExtractedAsset struct {
+ Call int
+ Type ExtractedAssetType
+ Reader io.Reader
+ Error error
+}
+
+func (p *Prog) ExtractAssets() []*ExtractedAsset {
+ handler := p.Target.ExtractMountedImage
+ if handler == nil {
+ // Such an operation is not supported by the target.
+ return nil
+ }
+ ret := []*ExtractedAsset{}
+ for id, c := range p.Calls {
+ // So far we only support the MountInRepro asset.
+ reader, err := handler(c)
+ if reader == nil && err == nil {
+ // This is not the call that contains the mount image.
+ continue
+ }
+ ret = append(ret, &ExtractedAsset{
+ Type: MountInRepro,
+ Call: id,
+ Reader: reader,
+ Error: err,
+ })
+ }
+ return ret
+}
diff --git a/prog/target.go b/prog/target.go
index 56c2a94ca..bc619d5b6 100644
--- a/prog/target.go
+++ b/prog/target.go
@@ -5,6 +5,7 @@ package prog
import (
"fmt"
+ "io"
"math/rand"
"sort"
"sync"
@@ -61,6 +62,11 @@ type Target struct {
SyscallMap map[string]*Syscall
ConstMap map[string]uint64
+ // The extracted images will be then saved to the disk or uploaded to the asset storage.
+ // Returns nil if the call does not mount any image.
+ // We have to use io.Reader since such blobs can get pretty large.
+ ExtractMountedImage func(c *Call) (io.Reader, error)
+
init sync.Once
initArch func(target *Target)
types []Type
diff --git a/sys/linux/init.go b/sys/linux/init.go
index 9080cbd7c..09b70f092 100644
--- a/sys/linux/init.go
+++ b/sys/linux/init.go
@@ -54,6 +54,7 @@ func InitTarget(target *prog.Target) {
target.MakeDataMmap = targets.MakePosixMmap(target, true, true)
target.Neutralize = arch.neutralize
+ target.ExtractMountedImage = arch.extractSyzMountImage
target.SpecialTypes = map[string]func(g *prog.Gen, typ prog.Type, dir prog.Dir, old prog.Arg) (
prog.Arg, []*prog.Call){
"timespec": arch.generateTimespec,
diff --git a/sys/linux/init_images.go b/sys/linux/init_images.go
index e914b8b4d..eb1d7c7ad 100644
--- a/sys/linux/init_images.go
+++ b/sys/linux/init_images.go
@@ -4,12 +4,45 @@
package linux
import (
+ "bytes"
"fmt"
"io"
+ "sort"
"github.com/google/syzkaller/prog"
)
+func (arch *arch) extractSyzMountImage(c *prog.Call) (io.Reader, error) {
+ // In order to reduce the size of syzlang programs, disk imags are de facto compressed.
+ // Here we do the uncompression.
+ if c.Meta.CallName != "syz_mount_image" {
+ return nil, nil
+ }
+ ret, err := parseSyzMountImage(c)
+ if err != nil {
+ // Parsing failed --> do not try to recover, just ignore.
+ return nil, err
+ } else if len(ret.segments) == 0 {
+ return nil, fmt.Errorf("an empty image")
+ }
+ readers := []io.Reader{}
+ nextPos := 0
+ // Add fake zero readers between segments, so that we can combine them to read the whole image.
+ for _, segment := range ret.segments {
+ offset := int(segment.offset.Val)
+ if offset > nextPos {
+ readers = append(readers, &zeroReader{left: offset - nextPos})
+ }
+ size := int(segment.size.Val)
+ readers = append(readers, bytes.NewReader(segment.data.Data()[0:size]))
+ nextPos = offset + size
+ }
+ if int(ret.size.Val) > nextPos {
+ readers = append(readers, &zeroReader{left: int(ret.size.Val) - nextPos})
+ }
+ return io.MultiReader(readers...), nil
+}
+
type zeroReader struct {
left int
}
@@ -25,39 +58,67 @@ func (zr *zeroReader) Read(p []byte) (n int, err error) {
for i := 0; i < toRead; i++ {
p[i] = 0
}
+ zr.left -= toRead
return toRead, nil
}
-func newZeroReader(size int) io.Reader {
- return &zeroReader{left: size}
-}
-
const imageMaxSize = 129 << 20
func fixUpImageSegments(parsed *mountImageArgs) {
+ const maxImageSegments = 4096
parsed.filterSegments(func(i int, segment *mountImageSegment) bool {
+ if i >= maxImageSegments {
+ // We don't want more than maxImageSegments segments.
+ return false
+ }
if segment.parseError != nil {
+ // Delete mangled segment structures.
return false
}
return segment.offset.Val < imageMaxSize && segment.size.Val < imageMaxSize
})
+ // Overwriting of the image multiple times is not efficient and complicates image extraction in Go.
+ // So let's make segments non-overlapping.
+ sort.Stable(parsed)
+ resizeSegment := func(s *mountImageSegment, size uint64) {
+ s.size.Val = size
+ s.data.SetData(s.data.Data()[0:size])
+ }
+
newSize := parsed.size.Val
- for _, segment := range parsed.segments {
+ if newSize > imageMaxSize {
+ newSize = imageMaxSize
+ }
+
+ for idx, segment := range parsed.segments {
actualSize := uint64(len(segment.data.Data()))
- if segment.size.Val > actualSize {
+ if segment.size.Val != actualSize {
segment.size.Val = actualSize
}
+ if idx > 0 {
+ // Adjust the end of the previous segment.
+ prevSegment := parsed.segments[idx-1]
+ if prevSegment.offset.Val+prevSegment.size.Val > segment.offset.Val {
+ resizeSegment(prevSegment, segment.offset.Val-prevSegment.offset.Val)
+ }
+ }
if segment.offset.Val+segment.size.Val > imageMaxSize {
- segment.offset.Val = imageMaxSize - segment.size.Val
+ resizeSegment(segment, imageMaxSize-segment.offset.Val)
}
if segment.offset.Val+segment.size.Val > newSize {
newSize = segment.offset.Val + segment.size.Val
}
}
if newSize > imageMaxSize {
- newSize = imageMaxSize
+ // Assert that the logic above is not broken.
+ panic("newSize > imageMaxSize")
}
parsed.size.Val = newSize
+
+ // Drop 0-size segments.
+ parsed.filterSegments(func(i int, segment *mountImageSegment) bool {
+ return segment.size.Val > 0
+ })
}
func (arch *arch) fixUpSyzMountImage(c *prog.Call) {
@@ -71,10 +132,6 @@ func (arch *arch) fixUpSyzMountImage(c *prog.Call) {
deactivateSyzMountImage(c)
return
}
- const maxImageSegments = 4096
- ret.filterSegments(func(i int, _ *mountImageSegment) bool {
- return i < maxImageSegments
- })
fixUpImageSegments(ret)
}
@@ -99,6 +156,20 @@ func (m *mountImageArgs) filterSegments(filter func(int, *mountImageSegment) boo
m.segmentsCount.Val = uint64(len(newArgs))
}
+// Methods for segment sorting.
+func (m *mountImageArgs) Len() int { return len(m.segments) }
+func (m *mountImageArgs) Swap(i, j int) {
+ inner := m.segmentsGroup.Inner
+ inner[i], inner[j] = inner[j], inner[i]
+ m.segments[i], m.segments[j] = m.segments[j], m.segments[i]
+}
+func (m *mountImageArgs) Less(i, j int) bool {
+ if m.segments[i].offset.Val != m.segments[j].offset.Val {
+ return m.segments[i].offset.Val < m.segments[j].offset.Val
+ }
+ return m.segments[i].size.Val < m.segments[j].size.Val
+}
+
type mountImageSegment struct {
data *prog.DataArg
offset *prog.ConstArg
diff --git a/sys/linux/init_images_test.go b/sys/linux/init_images_test.go
index 816d59902..0bfb68cc4 100644
--- a/sys/linux/init_images_test.go
+++ b/sys/linux/init_images_test.go
@@ -4,12 +4,24 @@
package linux
import (
+ "flag"
+ "fmt"
+ "io"
+ "math/rand"
+ "os"
+ "path/filepath"
+ "reflect"
+ "sort"
+ "strings"
"testing"
+ "time"
+ "github.com/google/syzkaller/pkg/osutil"
"github.com/google/syzkaller/prog"
"github.com/google/syzkaller/sys/targets"
)
+// nolint: lll
func TestSyzMountImageNeutralize(t *testing.T) {
prog.TestDeserializeHelper(t, targets.Linux, targets.AMD64, nil, []prog.DeserializeTest{
{
@@ -23,16 +35,118 @@ func TestSyzMountImageNeutralize(t *testing.T) {
Out: `syz_mount_image$bfs(&(0x7f0000000000)='bfs\x00', &(0x7f0000000100)='./file1\x00', 0x240, 0x2, &(0x7f0000000200)=[{&(0x7f0000010000)="cefaad1bc0210000ff0f0000ffffffffffffffffffffffffffffffff73797a6b616c73797a6b616c00"/64, 0x40, 0x0}, {&(0x7f0000010040)="0200000011000000140000001f22000002000000ed4100000000000001000000020000005ffb19635ffb19635ffb196300"/64, 0x40, 0x200}], 0x0, &(0x7f00000100a0)={[], [], 0x0}, 0x0)`,
},
{
- // Overflow over the max image size.
- In: `syz_mount_image$bfs(&(0x7f0000000000)='bfs\x00', &(0x7f0000000100)='./file2\x00', 0x8200000, 0x2, &(0x7f0000000200)=[{&(0x7f0000010000)="cefaad1bc0210000ff0f0000ffffffffffffffffffffffffffffffff73797a6b616c73797a6b616c00"/64, 0x40, 0x0}, {&(0x7f0000010040)="0200000011000000140000001f22000002000000ed4100000000000001000000020000005ffb19635ffb19635ffb196300"/64, 0x400, 0x80fffff}], 0x0, &(0x7f00000100a0)={[], [], 0x0}, 0x0)`,
- // It should shift the overflowing segment and adjust the total size.
- Out: `syz_mount_image$bfs(&(0x7f0000000000)='bfs\x00', &(0x7f0000000100)='./file2\x00', 0x8100000, 0x2, &(0x7f0000000200)=[{&(0x7f0000010000)="cefaad1bc0210000ff0f0000ffffffffffffffffffffffffffffffff73797a6b616c73797a6b616c00"/64, 0x40, 0x0}, {&(0x7f0000010040)="0200000011000000140000001f22000002000000ed4100000000000001000000020000005ffb19635ffb19635ffb196300"/64, 0x40, 0x80fffc0}], 0x0, &(0x7f00000100a0)={[], [], 0x0}, 0x0)`,
- },
- {
// Invalid offset.
In: `syz_mount_image$bfs(&(0x7f0000000000)='bfs\x00', &(0x7f0000000100)='./file1\x00', 0x20, 0x2, &(0x7f0000000200)=[{&(0x7f0000010000)="cefaad1bc0210000ff0f0000ffffffffffffffffffffffffffffffff73797a6b616c73797a6b616c00"/64, 0x40, 0x0}, {&(0x7f0000010040)="0200000011000000140000001f22000002000000ed4100000000000001000000020000005ffb19635ffb19635ffb196300"/64, 0x40, 0x9100000}], 0x0, &(0x7f00000100a0)={[], [], 0x0}, 0x0)`,
// The segment is deleted.
Out: `syz_mount_image$bfs(&(0x7f0000000000)='bfs\x00', &(0x7f0000000100)='./file1\x00', 0x40, 0x1, &(0x7f0000000200)=[{&(0x7f0000010000)="cefaad1bc0210000ff0f0000ffffffffffffffffffffffffffffffff73797a6b616c73797a6b616c00"/64, 0x40, 0x0}], 0x0, &(0x7f00000100a0)={[], [], 0x0}, 0x0)`,
},
+ {
+ // Overlapping and unsorted segments.
+ In: `syz_mount_image$bfs(&(0x7f0000000000)='bfs\x00', &(0x7f0000000100)='./file0\x00', 0x2220, 0x3, &(0x7f0000000200)=[{&(0x7f0000010000)="cafef00d"/64, 0x50, 0x20}, {&(0x7f0000010040)="deadbeef"/64, 0x30, 0x10}, {&(0x7f0000010080)="abcdef"/64, 0x40, 0x20}], 0x0, &(0x7f00000100a0)={[], [], 0x0}, 0x0)`,
+ Out: `syz_mount_image$bfs(&(0x7f0000000000)='bfs\x00', &(0x7f0000000100)='./file0\x00', 0x2220, 0x2, &(0x7f0000000200)=[{&(0x7f0000010040)="deadbeef00"/16, 0x10, 0x10}, {&(0x7f0000010000)="cafef00d00"/64, 0x40, 0x20}], 0x0, &(0x7f00000100a0)={[], [], 0x0}, 0x0)`,
+ },
})
}
+
+var flagUpdate = flag.Bool("update", false, "update test files accordingly to current results")
+
+func TestExtractSyzMountImage(t *testing.T) {
+ target, err := prog.GetTarget(targets.Linux, targets.AMD64)
+ if err != nil {
+ t.Fatal(err)
+ }
+ files, err := filepath.Glob(filepath.Join("testdata", "fs_images", "*.in"))
+ if err != nil {
+ t.Fatalf("directory read failed: %v", err)
+ }
+ allOutFiles, err := filepath.Glob(filepath.Join("testdata", "fs_images", "*.out*"))
+ if err != nil {
+ t.Fatalf("directory read failed: %v", err)
+ }
+ testedOutFiles := []string{}
+ for _, file := range files {
+ if !strings.HasSuffix(file, ".in") {
+ continue
+ }
+ sourceProg, err := os.ReadFile(file)
+ if err != nil {
+ t.Fatal(err)
+ }
+ p, err := target.Deserialize(sourceProg, prog.NonStrict)
+ if err != nil {
+ t.Fatalf("failed to deserialize %s: %s", file, err)
+ }
+ base := strings.TrimSuffix(file, ".in")
+ for _, asset := range p.ExtractAssets() {
+ if asset.Type != prog.MountInRepro {
+ continue
+ }
+ outFilePath := fmt.Sprintf("%s.out%d", base, asset.Call)
+ var testResult []byte
+ if asset.Reader != nil {
+ var err error
+ testResult, err = io.ReadAll(asset.Reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+ if *flagUpdate && asset.Reader != nil {
+ err := osutil.WriteFile(outFilePath, testResult)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+ outExists := osutil.IsExist(outFilePath)
+ if !outExists && asset.Reader != nil {
+ t.Fatalf("#%d: mount found, but does not exist in the answer", asset.Call)
+ }
+ if testResult != nil {
+ testedOutFiles = append(testedOutFiles, outFilePath)
+ outFile, err := os.ReadFile(outFilePath)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(testResult, outFile) {
+ t.Fatalf("output not equal:\nWant: %x\nGot: %x", outFile, testResult)
+ }
+ }
+ }
+ }
+ sort.Strings(testedOutFiles)
+ sort.Strings(allOutFiles)
+ if !reflect.DeepEqual(testedOutFiles, allOutFiles) {
+ t.Fatalf("all out files: %v\ntested files: %v", allOutFiles, testedOutFiles)
+ }
+}
+
+// nolint: lll
+func TestSyzMountImageMutation(t *testing.T) {
+ // We cannot unfortunately just import InitTest from prog.
+ rs := rand.NewSource(time.Now().UnixNano())
+ iters := 100
+ target, err := prog.GetTarget("linux", "amd64")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var p *prog.Prog
+ var ct *prog.ChoiceTable
+
+ const mutateCount = 1000
+ const baseProg = `syz_mount_image$bfs(&(0x7f0000000000)='bfs\x00', &(0x7f0000000100)='./file0\x00', 0x2220, 0x2, &(0x7f0000000200)=[{&(0x7f0000010040)="deadbeef00"/16, 0x10, 0x10}, {&(0x7f0000010000)="cafef00d00"/64, 0x40, 0x20}], 0x0, &(0x7f00000100a0)={[], [], 0x0}, 0x0)`
+
+ for i := 0; i < iters; i++ {
+ if i%mutateCount == 0 {
+ var err error
+ p, err = target.Deserialize([]byte(baseProg), prog.NonStrict)
+ if err != nil {
+ t.Fatal(err)
+ }
+ ct = target.DefaultChoiceTable()
+ }
+ p.Mutate(rs, 1, ct, nil, nil)
+ // We only call the extraction code and do mutations to catch possible panics.
+ // It is absolutely normal for syzkaller to mutate the call to the level when the image can no longer be extracted.
+ p.Target.ExtractMountedImage(p.Calls[0])
+ }
+}
diff --git a/sys/linux/testdata/fs_images/0.in b/sys/linux/testdata/fs_images/0.in
new file mode 100644
index 000000000..3eed397dd
--- /dev/null
+++ b/sys/linux/testdata/fs_images/0.in
@@ -0,0 +1 @@
+syz_mount_image$bfs(&(0x7f0000000000)='bfs\x00', &(0x7f0000000100)='./file0\x00', 0x2220, 0x3, &(0x7f0000000200)=[{&(0x7f0000010040)="deadbeef00"/64, 0x10, 0x10}, {&(0x7f0000010000)="cafef00d00"/64, 0x0, 0x20}, {&(0x7f0000010080)="abcdef00"/64, 0x40, 0x20}], 0x0, &(0x7f00000100a0)={[], [], 0x0}) \ No newline at end of file
diff --git a/sys/linux/testdata/fs_images/0.out0 b/sys/linux/testdata/fs_images/0.out0
new file mode 100644
index 000000000..424d24bd8
--- /dev/null
+++ b/sys/linux/testdata/fs_images/0.out0
Binary files differ
diff --git a/sys/linux/testdata/fs_images/1.in b/sys/linux/testdata/fs_images/1.in
new file mode 100644
index 000000000..3d9d3e222
--- /dev/null
+++ b/sys/linux/testdata/fs_images/1.in
@@ -0,0 +1 @@
+syz_mount_image$ext4(&(0x7f0000000000)='ext4\x00', &(0x7f0000000100)='./file0\x00', 0x40000, 0x2a, &(0x7f0000000200)=[{&(0x7f0000010000)="20000000000100000c000000ce0000000f00000001000000000000000000000000200000002000002000000067fb196367fb19630100ffff53ef01000100000067fb1963000000000000000001000000000000000b000000800000000800000052470000620100000000000000000000000000000000000073797a6b616c6c6572000000000000002f746d702f73797a2d696d61676567656e333234303537373133330000000000000000000000000000000000000000000000000000000000", 0xc0, 0x400}, {&(0x7f00000100c0)="0000000000000000000000009bbd22de26804913867de6e04e8059bc010000000c0000000000000067fb19630000000000000000000000000000000000000000", 0x40, 0x4e0}, {&(0x7f0000010100)="0100000000000500110000000000000000000000040000003c00000000000000", 0x20, 0x560}, {&(0x7f0000010120)="0300000004000000090000000000000000000000000000000000000000000000", 0x20, 0x640}, {&(0x7f0000010140)="030000001300000023000000ce000f0003000400000000000000000000000000", 0x20, 0x800}, {&(0x7f0000010160)="fffffffffcff0700000000000000000000000000000000000000000000000080ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff020000000c0001022e000000020000000c0002022e2e00000b00000014000a026c6f73742b666f756e6400000c0000001000050266696c65300000000f0000001000050166696c6531000000100000001000050166696c6532000000100000001000050166696c6533000000110000009403090166696c652e636f6c64000000", 0x480, 0xc00}, {&(0x7f00000105e0)="0b0000000c0001022e00000002000000f40302022e2e00000000000000000000", 0x20, 0x1400}, {&(0x7f0000010600)="0000000000040000000000000000000000000000000000000000000000000000", 0x20, 0x1800}, {&(0x7f0000010620)="0000000000040000000000000000000000000000000000000000000000000000", 0x20, 0x1c00}, {&(0x7f0000010640)="0000000000040000000000000000000000000000000000000000000000000000", 0x20, 0x2000}, {&(0x7f0000010660)="0000000000040000000000000000000000000000000000000000000000000000", 0x20, 0x2400}, {&(0x7f0000010680)="0000000000040000000000000000000000000000000000000000000000000000", 0x20, 0x2800}, {&(0x7f00000106a0)="0000000000040000000000000000000000000000000000000000000000000000", 0x20, 0x2c00}, {&(0x7f00000106c0)="0000000000040000000000000000000000000000000000000000000000000000", 0x20, 0x3000}, {&(0x7f00000106e0)="0000000000040000000000000000000000000000000000000000000000000000", 0x20, 0x3400}, {&(0x7f0000010700)="0000000000040000000000000000000000000000000000000000000000000000", 0x20, 0x3800}, {&(0x7f0000010720)="0000000000040000000000000000000000000000000000000000000000000000", 0x20, 0x3c00}, {&(0x7f0000010740)="0000000000040000000000000000000000000000000000000000000000000000", 0x20, 0x4000}, {&(0x7f0000010760)="504d4d00504d4dff67fb1963000000006872757476696b2e632e676f6f676c6572732e636f6d0000000000000000000000000000000000000000000000000000000000000000000000000000000000006c6f6f7032310075782f746573742f73797a5f6d6f756e745f696d6167655f6505000000000000000000000000000000", 0x80, 0x4400}, {&(0x7f00000107e0)="111fc0d901000000803a0900803a090000000000060000000000000005000000", 0x20, 0x4800}, {&(0x7f0000010800)="ffff0100ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300000000000000000000000000000000000000000000000000000000000000", 0x420, 0x4c00}, {&(0x7f0000010c20)="0400000000000000000000000000000000000000000000000000000000000000", 0x20, 0x5400}, {&(0x7f0000010c40)="0500000000000000000000000000000000000000000000000000000000000000", 0x20, 0x5800}, {&(0x7f0000010c60)="000000000000000001000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000", 0x60, 0x5c00}, {&(0x7f0000010cc0)="0200000000000000000000000000000000000000000000000000000000000000", 0x20, 0x6000}, {&(0x7f0000010ce0)="2719c0d901000000803a0900803a090000000000060000000000000005000000", 0x20, 0x6400}, {&(0x7f0000010d00)="0300000000000000000000000000000000000000000000000000000000000000", 0x20, 0x6800}, {&(0x7f0000010d20)="0400000000000000000000000000000000000000000000000000000000000000", 0x20, 0x6c00}, {&(0x7f0000010d40)="0500000000000000000000000000000000000000000000000000000000000000", 0x20, 0x7000}, {&(0x7f0000010d60)="000000000000000001000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000", 0x60, 0x7400}, {&(0x7f0000010dc0)="0200000000000000000000000000000000000000000000000000000000000000", 0x20, 0x7800}, {&(0x7f0000010de0)="0c0000000c0001022e000000020000000c0002022e2e00000d0000001000050166696c65300000000e000000d803050766696c65310000000000000000000000", 0x40, 0x7c00}, {&(0x7f0000010e20)="000002ea0100000001000000270f240c000000000000000000000000000000000601f8030000000006000000779b539778617474723100000601f003000000000600000074985397786174747232000000000000000000000000000000000000", 0x60, 0x8000}, {&(0x7f0000010e80)="0000000000000000000000000000000078617474723200007861747472310000", 0x20, 0x83e0}, {&(0x7f0000010ea0)="000000000000000067fb196367fb196367fb1963000000000000000000000000", 0x20, 0x8c00}, {&(0x7f0000010ec0)="ed4100000004000067fb196367fb196367fb196300000000000004000200000000000800050000000af301000400000000000000000000000100000004000000", 0x40, 0x8c80}, {&(0x7f0000010f00)="808100000018000067fb196367fb196367fb196300000000000001000c00000010000800000000000af30300040000000000000000000000010000001200000001000000010000001800000002000000040000001400000000000000000000000000000000000000000000000000000000000000000000000000000000000000808100000018000067fb196367fb196367fb196300000000000001000c00000010000800000000000af30300040000000000000000000000010000001900000001000000010000001e00000002000000040000001a0000000000000000000000", 0xe0, 0x8d00}, {&(0x7f0000010fe0)="c04100000030000067fb196367fb196367fb196300000000000002001800000000000800000000000af301000400000000000000000000000c00000005000000", 0x40, 0x9100}, {&(0x7f0000011020)="ed4100000004000067fb196367fb196367fb196300000000000002000200000000000800030000000af30100040000000000000000000000010000001f000000000000000000000000000000000000000000000000000000000000000000000000000000a6b0039d000000000000000000000000000000000000000000000000ed8100001a04000067fb196367fb196367fb196300000000000001000400000000000800010000000af3010004000000000000000000000002000000270000000000000000000000000000000000000000000000000000000000000000000000000000005109c476000000000000000000000000000000000000000000000000ffa100002700000067fb196367fb196367fb196300000000000001000000000000000000010000002f746d702f73797a2d696d61676567656e333234303537373133332f66696c65302f66696c653000000000000000000000000000000000000000000047fa2223000000000000000000000000000000000000000000000000ed8100000a00000067fb196367fb196367fb196300000000000001000400000000000800010000000af301000400000000000000000000000100000029000000000000000000000000000000000000000000000000000000000000000000000000000000d10ea931200000000000000000000000000000000000000000000000ed8100002823000067fb196367fb196367fb196300000000000002001200000000000800010000000af30100040000000000000000000000090000002a0000000000000000000000000000000000000000000000000000000000000000000000000000006077b616000000000000000000000000000000000000000000000000ed8100006400000067fb196367fb196367fb196300000000000001000200000000000800010000000af3010004000000000000000000000001000000330000000000000000000000000000000000000000000000000000000000000000000000000000003ffd7303000000000000000000000000000000000000000000000000", 0x300, 0x9180}, {&(0x7f0000011320)="73797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c000000000000", 0x420, 0x9c00}, {&(0x7f0000011740)="73797a6b616c6c65727300000000000000000000000000000000000000000000", 0x20, 0xa400}, {&(0x7f0000011760)="73797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c657273797a6b616c6c65727300000000000000000000000000000000000000000000000000000000", 0x80, 0xcc00}], 0x0, &(0x7f00000117e0)) \ No newline at end of file
diff --git a/sys/linux/testdata/fs_images/1.out0 b/sys/linux/testdata/fs_images/1.out0
new file mode 100644
index 000000000..c498f4d53
--- /dev/null
+++ b/sys/linux/testdata/fs_images/1.out0
Binary files differ