mirror of
https://github.com/reactos/syzkaller.git
synced 2024-11-23 19:39:40 +00:00
cfc46d9d0b
Right now Arg is a huge struct (160 bytes), which has many different fields used for different arg kinds. Since most of the args we see in a typical corpus are ArgConst, this results in a significant memory overuse. This change: - makes Arg an interface instead of a struct - adds a SomethingArg struct for each arg kind we have - converts all *Arg pointers into just Arg, since interface variable by itself contains a pointer to the actual data - removes ArgPageSize, now ConstArg is used instead - consolidates correspondence between arg kinds and types, see comments before each SomethingArg struct definition - now LenType args that denote the length of VmaType args are serialized as "0x1000" instead of "(0x1000)"; to preserve backwards compatibility syzkaller is able to parse the old format for now - multiple small changes all over to make the above work After this change syzkaller uses twice less memory after deserializing a typical corpus.
192 lines
5.7 KiB
Go
192 lines
5.7 KiB
Go
// 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 prog
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/google/syzkaller/sys"
|
|
)
|
|
|
|
type CsumKind int
|
|
|
|
const (
|
|
CsumInet CsumKind = iota
|
|
)
|
|
|
|
type CsumChunkKind int
|
|
|
|
const (
|
|
CsumChunkArg CsumChunkKind = iota
|
|
CsumChunkConst
|
|
)
|
|
|
|
type CsumInfo struct {
|
|
Kind CsumKind
|
|
Chunks []CsumChunk
|
|
}
|
|
|
|
type CsumChunk struct {
|
|
Kind CsumChunkKind
|
|
Arg Arg // for CsumChunkArg
|
|
Value uintptr // for CsumChunkConst
|
|
Size uintptr // for CsumChunkConst
|
|
}
|
|
|
|
func getFieldByName(arg Arg, name string) Arg {
|
|
for _, field := range arg.(*GroupArg).Inner {
|
|
if field.Type().FieldName() == name {
|
|
return field
|
|
}
|
|
}
|
|
panic(fmt.Sprintf("failed to find %v field in %v", name, arg.Type().Name()))
|
|
}
|
|
|
|
func extractHeaderParamsIPv4(arg Arg) (Arg, Arg) {
|
|
srcAddr := getFieldByName(arg, "src_ip")
|
|
if srcAddr.Size() != 4 {
|
|
panic(fmt.Sprintf("src_ip field in %v must be 4 bytes", arg.Type().Name()))
|
|
}
|
|
dstAddr := getFieldByName(arg, "dst_ip")
|
|
if dstAddr.Size() != 4 {
|
|
panic(fmt.Sprintf("dst_ip field in %v must be 4 bytes", arg.Type().Name()))
|
|
}
|
|
return srcAddr, dstAddr
|
|
}
|
|
|
|
func extractHeaderParamsIPv6(arg Arg) (Arg, Arg) {
|
|
srcAddr := getFieldByName(arg, "src_ip")
|
|
if srcAddr.Size() != 16 {
|
|
panic(fmt.Sprintf("src_ip field in %v must be 4 bytes", arg.Type().Name()))
|
|
}
|
|
dstAddr := getFieldByName(arg, "dst_ip")
|
|
if dstAddr.Size() != 16 {
|
|
panic(fmt.Sprintf("dst_ip field in %v must be 4 bytes", arg.Type().Name()))
|
|
}
|
|
return srcAddr, dstAddr
|
|
}
|
|
|
|
func composePseudoCsumIPv4(tcpPacket, srcAddr, dstAddr Arg, protocol uint8, pid int) CsumInfo {
|
|
info := CsumInfo{Kind: CsumInet}
|
|
info.Chunks = append(info.Chunks, CsumChunk{CsumChunkArg, srcAddr, 0, 0})
|
|
info.Chunks = append(info.Chunks, CsumChunk{CsumChunkArg, dstAddr, 0, 0})
|
|
info.Chunks = append(info.Chunks, CsumChunk{CsumChunkConst, nil, uintptr(swap16(uint16(protocol))), 2})
|
|
info.Chunks = append(info.Chunks, CsumChunk{CsumChunkConst, nil, uintptr(swap16(uint16(tcpPacket.Size()))), 2})
|
|
info.Chunks = append(info.Chunks, CsumChunk{CsumChunkArg, tcpPacket, 0, 0})
|
|
return info
|
|
}
|
|
|
|
func composePseudoCsumIPv6(tcpPacket, srcAddr, dstAddr Arg, protocol uint8, pid int) CsumInfo {
|
|
info := CsumInfo{Kind: CsumInet}
|
|
info.Chunks = append(info.Chunks, CsumChunk{CsumChunkArg, srcAddr, 0, 0})
|
|
info.Chunks = append(info.Chunks, CsumChunk{CsumChunkArg, dstAddr, 0, 0})
|
|
info.Chunks = append(info.Chunks, CsumChunk{CsumChunkConst, nil, uintptr(swap32(uint32(tcpPacket.Size()))), 4})
|
|
info.Chunks = append(info.Chunks, CsumChunk{CsumChunkConst, nil, uintptr(swap32(uint32(protocol))), 4})
|
|
info.Chunks = append(info.Chunks, CsumChunk{CsumChunkArg, tcpPacket, 0, 0})
|
|
return info
|
|
}
|
|
|
|
func findCsummedArg(arg Arg, typ *sys.CsumType, parentsMap map[Arg]Arg) Arg {
|
|
if typ.Buf == "parent" {
|
|
if csummedArg, ok := parentsMap[arg]; ok {
|
|
return csummedArg
|
|
}
|
|
panic(fmt.Sprintf("parent for %v is not in parents map", typ.Name()))
|
|
} else {
|
|
for parent := parentsMap[arg]; parent != nil; parent = parentsMap[parent] {
|
|
if typ.Buf == parent.Type().Name() {
|
|
return parent
|
|
}
|
|
}
|
|
}
|
|
panic(fmt.Sprintf("csum field '%v' references non existent field '%v'", typ.FieldName(), typ.Buf))
|
|
}
|
|
|
|
func calcChecksumsCall(c *Call, pid int) map[Arg]CsumInfo {
|
|
var inetCsumFields []Arg
|
|
var pseudoCsumFields []Arg
|
|
|
|
// Find all csum fields.
|
|
foreachArgArray(&c.Args, nil, func(arg, base Arg, _ *[]Arg) {
|
|
if typ, ok := arg.Type().(*sys.CsumType); ok {
|
|
switch typ.Kind {
|
|
case sys.CsumInet:
|
|
inetCsumFields = append(inetCsumFields, arg)
|
|
case sys.CsumPseudo:
|
|
pseudoCsumFields = append(pseudoCsumFields, arg)
|
|
default:
|
|
panic(fmt.Sprintf("unknown csum kind %v\n", typ.Kind))
|
|
}
|
|
}
|
|
})
|
|
|
|
// Return if no csum fields found.
|
|
if len(inetCsumFields) == 0 && len(pseudoCsumFields) == 0 {
|
|
return nil
|
|
}
|
|
|
|
// Build map of each field to its parent struct.
|
|
parentsMap := make(map[Arg]Arg)
|
|
foreachArgArray(&c.Args, nil, func(arg, base Arg, _ *[]Arg) {
|
|
if _, ok := arg.Type().(*sys.StructType); ok {
|
|
for _, field := range arg.(*GroupArg).Inner {
|
|
parentsMap[InnerArg(field)] = arg
|
|
}
|
|
}
|
|
})
|
|
|
|
csumMap := make(map[Arg]CsumInfo)
|
|
|
|
// Calculate generic inet checksums.
|
|
for _, arg := range inetCsumFields {
|
|
typ, _ := arg.Type().(*sys.CsumType)
|
|
csummedArg := findCsummedArg(arg, typ, parentsMap)
|
|
chunk := CsumChunk{CsumChunkArg, csummedArg, 0, 0}
|
|
info := CsumInfo{Kind: CsumInet, Chunks: make([]CsumChunk, 0)}
|
|
info.Chunks = append(info.Chunks, chunk)
|
|
csumMap[arg] = info
|
|
}
|
|
|
|
// No need to continue if there are no pseudo csum fields.
|
|
if len(pseudoCsumFields) == 0 {
|
|
return csumMap
|
|
}
|
|
|
|
// Extract ipv4 or ipv6 source and destination addresses.
|
|
ipv4HeaderParsed := false
|
|
ipv6HeaderParsed := false
|
|
var ipSrcAddr Arg
|
|
var ipDstAddr Arg
|
|
foreachArgArray(&c.Args, nil, func(arg, base Arg, _ *[]Arg) {
|
|
// syz_csum_* structs are used in tests
|
|
switch arg.Type().Name() {
|
|
case "ipv4_header", "syz_csum_ipv4_header":
|
|
ipSrcAddr, ipDstAddr = extractHeaderParamsIPv4(arg)
|
|
ipv4HeaderParsed = true
|
|
case "ipv6_packet", "syz_csum_ipv6_header":
|
|
ipSrcAddr, ipDstAddr = extractHeaderParamsIPv6(arg)
|
|
ipv6HeaderParsed = true
|
|
}
|
|
})
|
|
if !ipv4HeaderParsed && !ipv6HeaderParsed {
|
|
panic("no ipv4 nor ipv6 header found")
|
|
}
|
|
|
|
// Calculate pseudo checksums.
|
|
for _, arg := range pseudoCsumFields {
|
|
typ, _ := arg.Type().(*sys.CsumType)
|
|
csummedArg := findCsummedArg(arg, typ, parentsMap)
|
|
protocol := uint8(typ.Protocol)
|
|
var info CsumInfo
|
|
if ipv4HeaderParsed {
|
|
info = composePseudoCsumIPv4(csummedArg, ipSrcAddr, ipDstAddr, protocol, pid)
|
|
} else {
|
|
info = composePseudoCsumIPv6(csummedArg, ipSrcAddr, ipDstAddr, protocol, pid)
|
|
}
|
|
csumMap[arg] = info
|
|
}
|
|
|
|
return csumMap
|
|
}
|