blob: 2a49bffe435745f6b37972f07d12e7ad371b2c52 [file] [log] [blame]
// Copyright 2018 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 linux
import (
"strings"
"github.com/google/syzkaller/prog"
)
func (arch *arch) generateIptables(g *prog.Gen, typ prog.Type, old prog.Arg) (
arg prog.Arg, calls []*prog.Call) {
return arch.generateNetfilterTable(g, typ, old, true, 5)
}
func (arch *arch) generateArptables(g *prog.Gen, typ prog.Type, old prog.Arg) (
arg prog.Arg, calls []*prog.Call) {
return arch.generateNetfilterTable(g, typ, old, false, 3)
}
func (arch *arch) generateNetfilterTable(g *prog.Gen, typ prog.Type, old prog.Arg,
hasUnion bool, hookCount int) (arg prog.Arg, calls []*prog.Call) {
const (
hookStart = 4
nonHookFields = 7
unused = uint64(^uint32(0))
)
if old == nil {
arg = g.GenerateSpecialArg(typ, &calls)
} else {
// TODO(dvyukov): try to restore original hook order after mutation
// instead of assigning brand new offsets.
arg = old
calls = g.MutateArg(arg)
}
var tableArg *prog.GroupArg
if hasUnion {
tableArg = arg.(*prog.UnionArg).Option.(*prog.GroupArg)
} else {
tableArg = arg.(*prog.GroupArg)
}
numFileds := nonHookFields + 2*hookCount
if len(tableArg.Inner) != numFileds {
panic("wrong number of fields in netfilter table")
}
entriesArg := tableArg.Inner[numFileds-1].(*prog.GroupArg)
if len(entriesArg.Inner) != 2 {
panic("netfilter entries is expected to have 2 fields")
}
entriesArray := entriesArg.Inner[0].(*prog.GroupArg)
// Collect offsets of entries.
offsets := make([]uint64, len(entriesArray.Inner))
var pos uint64
for i, entryArg := range entriesArray.Inner {
offsets[i] = pos
pos += entryArg.Size()
}
if pos != entriesArray.Size() {
panic("netfilter offsets are broken")
}
genOffset := func() uint64 {
if g.Rand().Intn(100) == 0 {
// Assign the underflow entry once in a while.
// We have it in underflow hooks, so no point in using it frequently.
return pos
}
return offsets[g.Rand().Intn(len(offsets))]
}
// Assign offsets to used hooks.
for hook := hookStart; hook < hookStart+hookCount; hook++ {
hookArg := tableArg.Inner[hook].(*prog.ConstArg)
if hookArg.Type().(*prog.ConstType).Val == unused {
continue // unused hook
}
hookArg.Val = genOffset()
}
// Assign offsets to used underflow entries.
for hook := hookStart + hookCount; hook < hookStart+2*hookCount; hook++ {
hookArg := tableArg.Inner[hook].(*prog.ConstArg)
if hookArg.Type().(*prog.ConstType).Val == unused {
continue // unused hook
}
hookArg.Val = pos
}
// Now update standard target jump offsets.
prog.ForeachSubArg(arg, func(arg prog.Arg, _ *prog.ArgCtx) {
if !strings.HasPrefix(arg.Type().Name(), `xt_target_t["", `) {
return
}
targetArg := arg.(*prog.GroupArg)
valArg := targetArg.Inner[3].(*prog.ConstArg)
flagsType, ok := valArg.Type().(*prog.FlagsType)
if !ok {
return
}
if int64(valArg.Val) < 0 {
for _, val := range flagsType.Vals {
if val == valArg.Val {
return // verdict
}
}
}
valArg.Val = genOffset()
})
return
}
func (arch *arch) generateEbtables(g *prog.Gen, typ prog.Type, old prog.Arg) (
arg prog.Arg, calls []*prog.Call) {
if old == nil {
arg = g.GenerateSpecialArg(typ, &calls)
} else {
// TODO(dvyukov): try to restore original hook order after mutation
// instead of assigning brand new offsets.
arg = old
calls = g.MutateArg(arg)
}
if g.Target().ArgContainsAny(arg) {
return
}
hooksField, entriesField := 4, 7
if g.Target().PtrSize == 8 {
// Account for paddings.
hooksField, entriesField = 5, 9
}
tableArg := arg.(*prog.UnionArg).Option.(*prog.GroupArg)
entriesPtr := tableArg.Inner[entriesField].(*prog.PointerArg)
if entriesPtr.Res == nil {
return
}
entriesArray := entriesPtr.Res.(*prog.GroupArg)
offsets := make([]uint64, len(entriesArray.Inner))
var pos, totalEntries uint64
for i, entriesArg0 := range entriesArray.Inner {
entriesArg := entriesArg0.(*prog.GroupArg)
arrayArg := entriesArg.Inner[len(entriesArg.Inner)-1].(*prog.GroupArg)
entriesArg.Inner[2].(*prog.ConstArg).Val = totalEntries
totalEntries += uint64(len(arrayArg.Inner))
offsets[i] = pos
pos += entriesArg.Size()
}
tableArg.Inner[2].(*prog.ConstArg).Val = totalEntries
if pos != entriesArray.Size() {
panic("netfilter offsets are broken")
}
// Assign offsets to used hooks.
validHooks := tableArg.Inner[1].(*prog.ConstArg).Val
hooksArg := tableArg.Inner[hooksField].(*prog.GroupArg)
for i, hookArg0 := range hooksArg.Inner {
hookArg := hookArg0.(*prog.ConstArg)
if validHooks&(1<<uint(i)) == 0 {
hookArg.Val = 0
continue
}
addr := g.Target().PhysicalAddr(entriesPtr)
if len(offsets) != 0 {
addr += offsets[0]
offsets = offsets[1:]
}
hookArg.Val = addr
}
// TODO(dvyukov): assign jump targets for targets.
return
}
func (arch *arch) sanitizeEbtables(c *prog.Call) {
// This is very hacky... just as netfilter interfaces.
// setsockopt's len argument must be equal to size of ebt_replace + entries size.
lenArg := c.Args[4].(*prog.ConstArg)
tablePtr := c.Args[3].(*prog.PointerArg).Res
if tablePtr == nil {
return
}
tableArg := tablePtr.(*prog.UnionArg).Option.(*prog.GroupArg)
entriesField := len(tableArg.Inner) - 1
entriesArg := tableArg.Inner[entriesField].(*prog.PointerArg).Res
if entriesArg == nil {
return
}
lenArg.Val = tableArg.Size() + entriesArg.Size()
}