aboutsummaryrefslogtreecommitdiffstats
path: root/sys/linux/init_images_test.go
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 /sys/linux/init_images_test.go
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.
Diffstat (limited to 'sys/linux/init_images_test.go')
-rw-r--r--sys/linux/init_images_test.go126
1 files changed, 120 insertions, 6 deletions
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])
+ }
+}