From 8cad1b789fd41ebbae53b5ffd4aefb21b738fdab Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Mon, 12 Jul 2021 14:48:15 +1000 Subject: pkg/ifuzz/powerpc: preload registers with interesting numbers GenerateInt() generates sort of random numbers for instruction fuzzer with focus on corner cases, let's use it for POWERPC too. Since we want memory access instruction to try these addresses, we preload generated values in GPRs used by just generated instruction. This in turn requires Insn::Encode() access for the instruction map to encode load instructions so this moves ld64() from the generator to insnSetMap and adds Insn::insnMap. This adds enc() to encode just the instruction without any randomization. This does not add additional instructions if cfg.MemRegions is empty so the ifuzz_test.go test still passes. Since EncodeParam() is not used by anything but Encode(), this open codes it. Signed-off-by: Alexey Kardashevskiy --- pkg/ifuzz/powerpc/powerpc.go | 82 +++++++++++++++++++++++++++++++++++--------- pkg/ifuzz/powerpc/pseudo.go | 42 ++--------------------- 2 files changed, 69 insertions(+), 55 deletions(-) diff --git a/pkg/ifuzz/powerpc/powerpc.go b/pkg/ifuzz/powerpc/powerpc.go index 56fb0a5b2..d87f073b6 100644 --- a/pkg/ifuzz/powerpc/powerpc.go +++ b/pkg/ifuzz/powerpc/powerpc.go @@ -35,6 +35,7 @@ type Insn struct { Opcode uint32 Mask uint32 + insnMap *insnSetMap generator func(cfg *iset.Config, r *rand.Rand) []byte } @@ -72,27 +73,23 @@ func encodeBits(n uint, f InsnBits) uint32 { return uint32((n & mask) << (31 - (f.Start + f.Length - 1))) } -func (insn *Insn) EncodeParam(v map[string]uint, r *rand.Rand) []byte { - insn32 := insn.Opcode - for reg, bits := range insn.Fields { - if val, ok := v[reg]; ok { - insn32 |= encodeBits(val, bits) - } else if r != nil { - insn32 |= encodeBits(uint(r.Intn(1<<16)), bits) - } - } - - ret := make([]byte, 4) - binary.LittleEndian.PutUint32(ret, insn32) - return ret -} - func (insn Insn) Encode(cfg *iset.Config, r *rand.Rand) []byte { if insn.Pseudo { return insn.generator(cfg, r) } - return insn.EncodeParam(nil, r) + ret := make([]byte, 0) + insn32 := insn.Opcode + for reg, bits := range insn.Fields { + field := uint(r.Intn(1 << 16)) + insn32 |= encodeBits(field, bits) + if len(cfg.MemRegions) != 0 && (reg == "RA" || reg == "RB") { + val := iset.GenerateInt(cfg, r, 8) + ret = append(ret, insn.insnMap.ld64(field, val)...) + } + } + + return append(ret, uint32toBytes(insn32)...) } func Register(insns []*Insn) { @@ -105,6 +102,7 @@ func Register(insns []*Insn) { } for _, insn := range insnset.Insns { insnset.insnMap[insn.Name] = insn + insn.insnMap = &insnset.insnMap } insnset.initPseudo() for _, insn := range insnset.Insns { @@ -123,3 +121,55 @@ func (insn Insn) mode() iset.Mode { } return (1 << iset.ModeLong64) | (1 << iset.ModeProt32) } + +func uint32toBytes(v uint32) []byte { + ret := make([]byte, 4) + binary.LittleEndian.PutUint32(ret, v) + + return ret +} + +func (insn *Insn) enc(v map[string]uint) []byte { + insn32 := insn.Opcode + for reg, bits := range insn.Fields { + if val, ok := v[reg]; ok { + insn32 |= encodeBits(val, bits) + } + } + return uint32toBytes(insn32) +} + +func (imap insnSetMap) ld64(reg uint, v uint64) []byte { + ret := make([]byte, 0) + + // This is a widely used macro to load immediate on ppc64 + // #define LOAD64(rn,name) + // addis rn,0,name##@highest \ lis rn,name##@highest + // ori rn,rn,name##@higher + // rldicr rn,rn,32,31 + // oris rn,rn,name##@h + // ori rn,rn,name##@l + ret = append(ret, imap["addis"].enc(map[string]uint{ + "RT": reg, + "RA": 0, // In "addis", '0' means 0, not GPR0 . + "SI": uint((v >> 48) & 0xffff)})...) + ret = append(ret, imap["ori"].enc(map[string]uint{ + "RA": reg, + "RS": reg, + "UI": uint((v >> 32) & 0xffff)})...) + ret = append(ret, imap["rldicr"].enc(map[string]uint{ + "RA": reg, + "RS": reg, + "SH": 32, + "ME": 31})...) + ret = append(ret, imap["oris"].enc(map[string]uint{ + "RA": reg, + "RS": reg, + "UI": uint((v >> 16) & 0xffff)})...) + ret = append(ret, imap["ori"].enc(map[string]uint{ + "RA": reg, + "RS": reg, + "UI": uint(v & 0xffff)})...) + + return ret +} diff --git a/pkg/ifuzz/powerpc/pseudo.go b/pkg/ifuzz/powerpc/pseudo.go index b1231092b..7529a16fc 100644 --- a/pkg/ifuzz/powerpc/pseudo.go +++ b/pkg/ifuzz/powerpc/pseudo.go @@ -62,48 +62,12 @@ func (gen *generator) byte(v []byte) { gen.text = append(gen.text, v...) } -func (gen *generator) ld64(reg uint, v uint64) { - // This is a widely used macro to load immediate on ppc64 - // #define LOAD64(rn,name) - // addis rn,0,name##@highest \ lis rn,name##@highest - // ori rn,rn,name##@higher - // rldicr rn,rn,32,31 - // oris rn,rn,name##@h - // ori rn,rn,name##@l - gen.byte(gen.imap["addis"].EncodeParam(map[string]uint{ - "RT": reg, - "RA": 0, // In "addis", '0' means 0, not GPR0 . - "SI": uint((v >> 48) & 0xffff)}, - nil)) - gen.byte(gen.imap["ori"].EncodeParam(map[string]uint{ - "RA": reg, - "RS": reg, - "UI": uint((v >> 32) & 0xffff)}, - nil)) - gen.byte(gen.imap["rldicr"].EncodeParam(map[string]uint{ - "RA": reg, - "RS": reg, - "SH": 32, - "ME": 31}, - nil)) - gen.byte(gen.imap["oris"].EncodeParam(map[string]uint{ - "RA": reg, - "RS": reg, - "UI": uint((v >> 16) & 0xffff)}, - nil)) - gen.byte(gen.imap["ori"].EncodeParam(map[string]uint{ - "RA": reg, - "RS": reg, - "UI": uint(v & 0xffff)}, - nil)) -} - func (gen *generator) sc(lev uint) { n := gen.r.Intn(9) // Valid hcall humbers at the momemt are: 4..0x450 - gen.ld64(3, uint64(gen.r.Intn(4+(0x450-4)/4))) + gen.byte(gen.imap.ld64(3, uint64(gen.r.Intn(4+(0x450-4)/4)))) for i := 4; i < n+4; i++ { - gen.ld64(uint(i), gen.r.Uint64()) + gen.byte(gen.imap.ld64(uint(i), gen.r.Uint64())) } - gen.byte(gen.imap["sc"].EncodeParam(map[string]uint{"LEV": lev}, nil)) + gen.byte(gen.imap["sc"].enc(map[string]uint{"LEV": lev})) } -- cgit mrf-deployment