syzkaller/prog/checksum.go
Dmitry Vyukov 58da4c35b1 prog: introduce Field type
Remvoe FieldName from Type and add a separate Field type
that holds field name. Use Field for struct fields, union options
and syscalls arguments, only these really have names.

Reduces size of sys/linux/gen/amd64.go from 5665583 to 5201321 (-8.2%).
Allows to not create new type for squashed any pointer.
But main advantages will follow, e.g. removing StructDesc,
using TypeRef in Arg, etc.

Update #1580
2020-05-02 12:16:06 +02:00

171 lines
5.2 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"
)
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 uint64 // for CsumChunkConst
Size uint64 // for CsumChunkConst
}
func calcChecksumsCall(c *Call) (map[Arg]CsumInfo, map[Arg]struct{}) {
var inetCsumFields, pseudoCsumFields []Arg
// Find all csum fields.
ForeachArg(c, func(arg Arg, _ *ArgCtx) {
if typ, ok := arg.Type().(*CsumType); ok {
switch typ.Kind {
case CsumInet:
inetCsumFields = append(inetCsumFields, arg)
case CsumPseudo:
pseudoCsumFields = append(pseudoCsumFields, arg)
default:
panic(fmt.Sprintf("unknown csum kind %v", typ.Kind))
}
}
})
if len(inetCsumFields) == 0 && len(pseudoCsumFields) == 0 {
return nil, nil
}
// Build map of each field to its parent struct.
parentsMap := make(map[Arg]Arg)
ForeachArg(c, func(arg Arg, _ *ArgCtx) {
if _, ok := arg.Type().(*StructType); ok {
for _, field := range arg.(*GroupArg).Inner {
parentsMap[InnerArg(field)] = arg
}
}
})
csumMap := make(map[Arg]CsumInfo)
csumUses := make(map[Arg]struct{})
// Calculate generic inet checksums.
for _, arg := range inetCsumFields {
typ, _ := arg.Type().(*CsumType)
csummedArg := findCsummedArg(arg, typ, parentsMap)
csumUses[csummedArg] = struct{}{}
chunk := CsumChunk{CsumChunkArg, csummedArg, 0, 0}
csumMap[arg] = CsumInfo{Kind: CsumInet, Chunks: []CsumChunk{chunk}}
}
// No need to continue if there are no pseudo csum fields.
if len(pseudoCsumFields) == 0 {
return csumMap, csumUses
}
// Extract ipv4 or ipv6 source and destination addresses.
var ipSrcAddr, ipDstAddr Arg
ForeachArg(c, func(arg Arg, _ *ArgCtx) {
groupArg, ok := arg.(*GroupArg)
if !ok {
return
}
// syz_csum_* structs are used in tests
switch groupArg.Type().TemplateName() {
case "ipv4_header", "syz_csum_ipv4_header":
ipSrcAddr, ipDstAddr = extractHeaderParams(groupArg, 4)
case "ipv6_packet_t", "syz_csum_ipv6_header":
ipSrcAddr, ipDstAddr = extractHeaderParams(groupArg, 16)
}
})
if ipSrcAddr == nil || ipDstAddr == nil {
panic("no ipv4 nor ipv6 header found")
}
// Calculate pseudo checksums.
for _, arg := range pseudoCsumFields {
typ, _ := arg.Type().(*CsumType)
csummedArg := findCsummedArg(arg, typ, parentsMap)
protocol := uint8(typ.Protocol)
var info CsumInfo
if ipSrcAddr.Size() == 4 {
info = composePseudoCsumIPv4(csummedArg, ipSrcAddr, ipDstAddr, protocol)
} else {
info = composePseudoCsumIPv6(csummedArg, ipSrcAddr, ipDstAddr, protocol)
}
csumMap[arg] = info
csumUses[csummedArg] = struct{}{}
csumUses[ipSrcAddr] = struct{}{}
csumUses[ipDstAddr] = struct{}{}
}
return csumMap, csumUses
}
func findCsummedArg(arg Arg, typ *CsumType, parentsMap map[Arg]Arg) Arg {
if typ.Buf == ParentRef {
csummedArg := parentsMap[arg]
if csummedArg == nil {
panic(fmt.Sprintf("%q for %q is not in parents map", ParentRef, typ.Name()))
}
return csummedArg
}
for parent := parentsMap[arg]; parent != nil; parent = parentsMap[parent] {
// TODO(dvyukov): support template argument names as in size calculation.
if typ.Buf == parent.Type().Name() {
return parent
}
}
panic(fmt.Sprintf("csum field %q references non existent field %q", typ.Name(), typ.Buf))
}
func composePseudoCsumIPv4(tcpPacket, srcAddr, dstAddr Arg, protocol uint8) 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, uint64(swap16(uint16(protocol))), 2})
info.Chunks = append(info.Chunks, CsumChunk{CsumChunkConst, nil, uint64(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) 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, uint64(swap32(uint32(tcpPacket.Size()))), 4})
info.Chunks = append(info.Chunks, CsumChunk{CsumChunkConst, nil, uint64(swap32(uint32(protocol))), 4})
info.Chunks = append(info.Chunks, CsumChunk{CsumChunkArg, tcpPacket, 0, 0})
return info
}
func extractHeaderParams(arg *GroupArg, size uint64) (Arg, Arg) {
srcAddr := getFieldByName(arg, "src_ip")
dstAddr := getFieldByName(arg, "dst_ip")
if srcAddr.Size() != size || dstAddr.Size() != size {
panic(fmt.Sprintf("src/dst_ip fields in %v must be %v bytes", arg.Type().Name(), size))
}
return srcAddr, dstAddr
}
func getFieldByName(arg *GroupArg, name string) Arg {
typ := arg.Type().(*StructType)
for i, field := range arg.Inner {
if typ.Fields[i].Name == name {
return field
}
}
panic(fmt.Sprintf("failed to find %v field in %v", name, arg.Type().Name()))
}