diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2024-05-06 08:33:51 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2024-05-06 11:24:51 +0000 |
| commit | 441f4fcc08ae33f36e5a15a9dd5abde3f0797921 (patch) | |
| tree | b85c0f6661d52d537e6e39b2791f0d7022390fec /prog | |
| parent | 69f2eab004cdc5bce339d5359dcf234698153dc7 (diff) | |
prog: fix validation of DataMmapProg
Allow to serialize/deserialize DataMmapProg
and fix validation in debug mode.
Fixes #4750
Diffstat (limited to 'prog')
| -rw-r--r-- | prog/clone.go | 8 | ||||
| -rw-r--r-- | prog/encoding.go | 49 | ||||
| -rw-r--r-- | prog/encoding_test.go | 18 | ||||
| -rw-r--r-- | prog/mutation.go | 3 | ||||
| -rw-r--r-- | prog/prog.go | 3 | ||||
| -rw-r--r-- | prog/rand.go | 5 | ||||
| -rw-r--r-- | prog/validation.go | 32 |
7 files changed, 68 insertions, 50 deletions
diff --git a/prog/clone.go b/prog/clone.go index 6ec28e39e..d41f3c13c 100644 --- a/prog/clone.go +++ b/prog/clone.go @@ -12,6 +12,14 @@ func (p *Prog) Clone() *Prog { } func (p *Prog) cloneWithMap(newargs map[*ResultArg]*ResultArg) *Prog { + if p.isUnsafe { + // We could clone it, but since we prohibit mutation + // of unsafe programs, it's unclear why we would clone it. + // Note: this also covers cloning of corpus programs + // during mutation, so if this is removed, we may need + // additional checks during mutation. + panic("cloning of unsafe programs is not supposed to be done") + } p1 := &Prog{ Target: p.Target, Calls: cloneCalls(p.Calls, newargs), diff --git a/prog/encoding.go b/prog/encoding.go index 06099993f..af0bccfc0 100644 --- a/prog/encoding.go +++ b/prog/encoding.go @@ -256,7 +256,8 @@ func (target *Target) Deserialize(data []byte, mode DeserializeMode) (*Prog, err } }() strict := mode == Strict || mode == StrictUnsafe - p := newParser(target, data, strict) + unsafe := mode == StrictUnsafe || mode == NonStrictUnsafe + p := newParser(target, data, strict, unsafe) prog, err := p.parseProg() if err := p.Err(); err != nil { return nil, err @@ -270,7 +271,6 @@ func (target *Target) Deserialize(data []byte, mode DeserializeMode) (*Prog, err if err := prog.validateWithOpts(validationOptions{ // Don't validate auto-set conditional fields. We'll patch them later. ignoreTransient: true, - allowUnsafe: mode == StrictUnsafe || mode == NonStrictUnsafe, }); err != nil { return nil, err } @@ -278,7 +278,7 @@ func (target *Target) Deserialize(data []byte, mode DeserializeMode) (*Prog, err if p.autos != nil { p.fixupAutos(prog) } - if mode != StrictUnsafe { + if !unsafe { if err := prog.sanitize(!strict); err != nil { return nil, err } @@ -288,7 +288,8 @@ func (target *Target) Deserialize(data []byte, mode DeserializeMode) (*Prog, err func (p *parser) parseProg() (*Prog, error) { prog := &Prog{ - Target: p.target, + Target: p.target, + isUnsafe: p.unsafe, } for p.Scan() { if p.EOF() { @@ -860,31 +861,8 @@ func (p *parser) parseAddr() (uint64, uint64, error) { if err != nil { return 0, 0, fmt.Errorf("failed to parse addr: %q", pstr) } - if addr < encodingAddrBase { - return 0, 0, fmt.Errorf("address without base offset: %q", pstr) - } addr -= encodingAddrBase - // This is not used anymore, but left here to parse old programs. - if p.Char() == '+' || p.Char() == '-' { - minus := false - if p.Char() == '-' { - minus = true - p.Parse('-') - } else { - p.Parse('+') - } - ostr := p.Ident() - off, err := strconv.ParseUint(ostr, 0, 64) - if err != nil { - return 0, 0, fmt.Errorf("failed to parse addr offset: %q", ostr) - } - if minus { - off = -off - } - addr += off - } target := p.target - maxMem := target.NumPages * target.PageSize var vmaSize uint64 if p.Char() == '/' { p.Parse('/') @@ -898,11 +876,14 @@ func (p *parser) parseAddr() (uint64, uint64, error) { if vmaSize == 0 { vmaSize = target.PageSize } - if vmaSize > maxMem { - vmaSize = maxMem - } - if addr > maxMem-vmaSize { - addr = maxMem - vmaSize + if !p.unsafe { + maxMem := target.NumPages * target.PageSize + if vmaSize > maxMem { + vmaSize = maxMem + } + if addr > maxMem-vmaSize { + addr = maxMem - vmaSize + } } } p.Parse(')') @@ -1119,6 +1100,7 @@ func fromHexChar(v byte) (byte, bool) { type parser struct { target *Target strict bool + unsafe bool vars map[string]*ResultArg autos map[Arg]bool comment string @@ -1130,10 +1112,11 @@ type parser struct { e error } -func newParser(target *Target, data []byte, strict bool) *parser { +func newParser(target *Target, data []byte, strict, unsafe bool) *parser { p := &parser{ target: target, strict: strict, + unsafe: unsafe, vars: make(map[string]*ResultArg), data: data, } diff --git a/prog/encoding_test.go b/prog/encoding_test.go index 608f94371..64e10ef0f 100644 --- a/prog/encoding_test.go +++ b/prog/encoding_test.go @@ -10,6 +10,8 @@ import ( "reflect" "sort" "testing" + + "github.com/stretchr/testify/assert" ) func setToArray(s map[string]struct{}) []string { @@ -32,7 +34,7 @@ func TestSerializeData(t *testing.T) { } buf := new(bytes.Buffer) serializeData(buf, data, readable) - p := newParser(nil, buf.Bytes(), true) + p := newParser(nil, buf.Bytes(), true, false) if !p.Scan() { t.Fatalf("parser does not scan") } @@ -386,6 +388,18 @@ func TestSerializeDeserialize(t *testing.T) { }) } +func TestDeserializeDataMmapProg(t *testing.T) { + testEachTarget(t, func(t *testing.T, target *Target) { + p := target.DataMmapProg() + data := p.Serialize() + p2, err := target.Deserialize(data, StrictUnsafe) + assert.NoError(t, err) + data2 := p2.Serialize() + assert.Equal(t, string(data), string(data2)) + p.SerializeForExec() + }) +} + func TestSerializeDeserializeRandom(t *testing.T) { testEachTargetRandom(t, func(t *testing.T, target *Target, rs rand.Source, iters int) { ct := target.DefaultChoiceTable() @@ -545,7 +559,7 @@ func TestHasNext(t *testing.T) { {"abc", true}, } for _, testCase := range testCases { - p := newParser(nil, []byte(testCase.input), true) + p := newParser(nil, []byte(testCase.input), true, false) if !p.Scan() { t.Fatalf("parser does not scan") } diff --git a/prog/mutation.go b/prog/mutation.go index 8547be3cc..c6cd3c7cf 100644 --- a/prog/mutation.go +++ b/prog/mutation.go @@ -55,6 +55,9 @@ func (o MutateOpts) weight() int { func (p *Prog) MutateWithOpts(rs rand.Source, ncalls int, ct *ChoiceTable, noMutate map[int]bool, corpus []*Prog, opts MutateOpts) { + if p.isUnsafe { + panic("mutation of unsafe programs is not supposed to be done") + } totalWeight := opts.weight() r := newRand(p.Target, rs) if ncalls < len(p.Calls) { diff --git a/prog/prog.go b/prog/prog.go index 880e02eeb..29667d217 100644 --- a/prog/prog.go +++ b/prog/prog.go @@ -12,6 +12,9 @@ type Prog struct { Target *Target Calls []*Call Comments []string + + // Was deserialized using Unsafe mode, so can do unsafe things. + isUnsafe bool } func (p *Prog) CallName(call int) string { diff --git a/prog/rand.go b/prog/rand.go index 6c58f7649..c55d43a3f 100644 --- a/prog/rand.go +++ b/prog/rand.go @@ -663,8 +663,9 @@ func (target *Target) GenSampleProg(meta *Syscall, rs rand.Source) *Prog { // Also used for testing as the simplest program. func (target *Target) DataMmapProg() *Prog { return &Prog{ - Target: target, - Calls: target.MakeDataMmap(), + Target: target, + Calls: target.MakeDataMmap(), + isUnsafe: true, } } diff --git a/prog/validation.go b/prog/validation.go index b2a358706..38cc3873e 100644 --- a/prog/validation.go +++ b/prog/validation.go @@ -26,23 +26,24 @@ func (p *Prog) validate() error { } type validCtx struct { - target *Target - opts validationOptions - args map[Arg]bool - uses map[Arg]Arg + target *Target + isUnsafe bool + opts validationOptions + args map[Arg]bool + uses map[Arg]Arg } type validationOptions struct { ignoreTransient bool - allowUnsafe bool // allow global file names, etc } func (p *Prog) validateWithOpts(opts validationOptions) error { ctx := &validCtx{ - target: p.Target, - opts: opts, - args: make(map[Arg]bool), - uses: make(map[Arg]Arg), + target: p.Target, + isUnsafe: p.isUnsafe, + opts: opts, + args: make(map[Arg]bool), + uses: make(map[Arg]Arg), } for i, c := range p.Calls { if c.Meta == nil { @@ -61,7 +62,7 @@ func (p *Prog) validateWithOpts(opts validationOptions) error { } func (ctx *validCtx) validateCall(c *Call) error { - if !ctx.opts.allowUnsafe && c.Meta.Attrs.Disabled { + if !ctx.isUnsafe && c.Meta.Attrs.Disabled { return fmt.Errorf("use of a disabled call") } if c.Props.Rerun > 0 && c.Props.FailNth > 0 { @@ -211,7 +212,7 @@ func (arg *DataArg) validate(ctx *validCtx, dir Dir) error { typ.Name(), arg.Size(), typ.TypeSize) } case BufferFilename: - if !ctx.opts.allowUnsafe && escapingFilename(string(arg.data)) { + if !ctx.isUnsafe && escapingFilename(string(arg.data)) { return fmt.Errorf("escaping filename %q", arg.data) } } @@ -286,11 +287,16 @@ func (arg *PointerArg) validate(ctx *validCtx, dir Dir) error { } } else { maxMem := ctx.target.NumPages * ctx.target.PageSize - size := arg.VmaSize + addr, size := arg.Address, arg.VmaSize if size == 0 && arg.Res != nil { size = arg.Res.Size() } - if arg.Address >= maxMem || arg.Address+size > maxMem { + if ctx.isUnsafe { + // Allow mapping 2 surrounding pages for DataMmapProg. + addr += ctx.target.PageSize + maxMem += 2 * ctx.target.PageSize + } + if addr >= maxMem || addr+size > maxMem { return fmt.Errorf("ptr %v has bad address %v/%v/%v", arg.Type().Name(), arg.Address, arg.VmaSize, size) } |
