// Copyright 2017 syzkaller project authors. All rights reserved. // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. package ifuzz import ( "encoding/hex" "math/rand" "testing" "github.com/google/syzkaller/pkg/ifuzz/iset" "github.com/google/syzkaller/pkg/testutil" ) var allArches = []string{ArchX86, ArchPowerPC, ArchArm64} func TestMode(t *testing.T) { for _, arch := range allArches { t.Run(arch, func(t *testing.T) { testMode(t, arch) }) } } func testMode(t *testing.T, arch string) { all := make(map[iset.Insn]bool) for mode := iset.Mode(0); mode < iset.ModeLast; mode++ { for priv := 0; priv < 2; priv++ { for exec := 0; exec < 2; exec++ { insns := allInsns(arch, mode, priv != 0, exec != 0) t.Logf("mode=%v priv=%v exec=%v: %v instructions", mode, priv, exec, len(insns)) for _, insn := range insns { all[insn] = true } } } } t.Logf("total: %v instructions", len(all)) } func TestDecode(t *testing.T) { for _, arch := range allArches { t.Run(arch, func(t *testing.T) { testDecode(t, arch) }) } } func testDecode(t *testing.T, arch string) { insnset := iset.Arches[arch] xedEnabled := false if _, err := insnset.DecodeExt(0, nil); err == nil { xedEnabled = true } r := rand.New(testutil.RandSource(t)) for repeat := 0; repeat < 10; repeat++ { for mode := iset.Mode(0); mode < iset.ModeLast; mode++ { cfg := &iset.Config{ Mode: mode, Priv: true, Exec: true, } failed := false for _, insn := range allInsns(arch, mode, true, true) { name, _, pseudo, _ := insn.Info() text0 := insn.Encode(cfg, r) text := text0 repeat: size, err := insnset.Decode(mode, text) if err != nil { t.Errorf("decoding %v %v failed (mode=%v): %v", name, hex.EncodeToString(text), mode, err) if len(text) != len(text0) { t.Errorf("whole: %v", hex.EncodeToString(text0)) } failed = true continue } if xedEnabled { xedSize, xedErr := insnset.DecodeExt(mode, text) if xedErr != nil { t.Errorf("xed decoding %v %v failed (mode=%v): %v", name, hex.EncodeToString(text), mode, xedErr) if len(text) != len(text0) { t.Errorf("whole: %v", hex.EncodeToString(text0)) } failed = true continue } if size != xedSize { t.Errorf("decoding %v %v failed (mode=%v): decoded %v/%v, xed decoded %v/%v", name, hex.EncodeToString(text), mode, size, xedSize, size, len(text)) if len(text) != len(text0) { t.Errorf("whole: %v", hex.EncodeToString(text0)) } failed = true continue } } if pseudo && size >= 0 && size < len(text) { text = text[size:] goto repeat } if size != len(text) { t.Errorf("decoding %v %v failed (mode=%v): decoded %v/%v", name, hex.EncodeToString(text), mode, size, len(text)) if len(text) != len(text0) { t.Errorf("whole: %v", hex.EncodeToString(text0)) } failed = true } } if failed { return } } } } func TestGenerate(t *testing.T) { for _, arch := range allArches { t.Run(arch, func(t *testing.T) { testGenerate(t, arch) }) } } func testGenerate(t *testing.T, arch string) { insnset := iset.Arches[arch] r := rand.New(testutil.RandSource(t)) for mode := iset.Mode(0); mode < iset.ModeLast; mode++ { for repeat := 1; repeat < 10; repeat++ { if len(insnset.GetInsns(mode, iset.TypeUser)) == 0 { continue } cfg := &iset.Config{ Arch: arch, Mode: mode, Priv: true, Exec: true, Len: repeat, } text := Generate(cfg, r) for len(text) != 0 { size, err := insnset.Decode(mode, text) if size == 0 || err != nil { t.Errorf("failed to decode text: % x", text) break } text = text[size:] } } } } func allInsns(arch string, mode iset.Mode, priv, exec bool) []iset.Insn { insnset := iset.Arches[arch] insns := insnset.GetInsns(mode, iset.TypeUser) if priv { insns = append(insns, insnset.GetInsns(mode, iset.TypePriv)...) if exec { insns = append(insns, insnset.GetInsns(mode, iset.TypeExec)...) } } return insns }