From 306ca0571c5d906ce76df97bd1ea54f4e0e50240 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Sat, 7 Jul 2018 20:07:30 +0200 Subject: prog, pkg/compiler: support fmt type fmt type allows to convert intergers and resources to string representation. --- pkg/compiler/check.go | 12 +++-- pkg/compiler/compiler.go | 2 +- pkg/compiler/gen.go | 6 ++- pkg/compiler/testdata/all.txt | 13 +++++ pkg/compiler/testdata/errors.txt | 12 +++++ pkg/compiler/types.go | 104 +++++++++++++++++++++++++++++++++------ pkg/csource/csource.go | 94 ++++++++++++++++++++++++----------- pkg/csource/linux_common.go | 10 +--- 8 files changed, 195 insertions(+), 58 deletions(-) (limited to 'pkg') diff --git a/pkg/compiler/check.go b/pkg/compiler/check.go index 19e196adb..a07bcd479 100644 --- a/pkg/compiler/check.go +++ b/pkg/compiler/check.go @@ -309,7 +309,7 @@ func (comp *compiler) checkLenType(t *ast.Type, name string, fields []*ast.Field if argDesc.Type == typeArgLenTarget { comp.checkLenTarget(t, name, arg.Ident, fields, parents) } else if argDesc.Type == typeArgType { - comp.checkLenType(arg, name, fields, parents, checked, false) + comp.checkLenType(arg, name, fields, parents, checked, argDesc.IsArg) } } } @@ -417,7 +417,7 @@ func (comp *compiler) collectUsedType(structs, flags, strflags map[string]bool, _, args, _ := comp.getArgsBase(t, "", prog.DirIn, isArg) for i, arg := range args { if desc.Args[i].Type == typeArgType { - comp.collectUsedType(structs, flags, strflags, arg, false) + comp.collectUsedType(structs, flags, strflags, arg, desc.Args[i].IsArg) } } } @@ -524,7 +524,7 @@ func (comp *compiler) checkTypeCtors(t *ast.Type, dir prog.Dir, isArg bool, _, args, _ := comp.getArgsBase(t, "", dir, isArg) for i, arg := range args { if desc.Args[i].Type == typeArgType { - comp.checkTypeCtors(arg, dir, false, ctors, checked) + comp.checkTypeCtors(arg, dir, desc.Args[i].IsArg, ctors, checked) } } } @@ -722,7 +722,11 @@ func (comp *compiler) checkType(ctx checkCtx, t *ast.Type, flags checkFlags) { err0 := comp.errors for i, arg := range args { if desc.Args[i].Type == typeArgType { - comp.checkType(ctx, arg, 0) + var innerFlags checkFlags + if desc.Args[i].IsArg { + innerFlags |= checkIsArg + } + comp.checkType(ctx, arg, innerFlags) } else { comp.checkTypeArg(t, arg, desc.Args[i]) } diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index b44a698ca..e66da802b 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -311,7 +311,7 @@ func (comp *compiler) foreachSubType(t *ast.Type, isArg bool, cb(t, desc, args, base) for i, arg := range args { if desc.Args[i].Type == typeArgType { - comp.foreachSubType(arg, false, cb) + comp.foreachSubType(arg, desc.Args[i].IsArg, cb) } } } diff --git a/pkg/compiler/gen.go b/pkg/compiler/gen.go index 5bf239e79..0e13db460 100644 --- a/pkg/compiler/gen.go +++ b/pkg/compiler/gen.go @@ -396,9 +396,13 @@ func genCommon(name, field string, size uint64, dir prog.Dir, opt bool) prog.Typ } func genIntCommon(com prog.TypeCommon, bitLen uint64, bigEndian bool) prog.IntTypeCommon { + bf := prog.FormatNative + if bigEndian { + bf = prog.FormatBigEndian + } return prog.IntTypeCommon{ TypeCommon: com, - BigEndian: bigEndian, + ArgFormat: bf, BitfieldLen: bitLen, } } diff --git a/pkg/compiler/testdata/all.txt b/pkg/compiler/testdata/all.txt index a5a097de3..573fb6412 100644 --- a/pkg/compiler/testdata/all.txt +++ b/pkg/compiler/testdata/all.txt @@ -213,3 +213,16 @@ u0 [ ] foo$u0(a ptr[in, u0]) + +# fmt + +foo$fmt0(a ptr[in, fmt[dec, int32[1:10]]]) +foo$fmt1(a ptr[in, fmt[hex, flags[int_flags]]]) +foo$fmt2(a ptr[in, fmt[oct, len[b]]], b ptr[in, array[int8]]) +foo$fmt3(a ptr[in, fmt[dec, proc[10, 20]]]) +foo$fmt4(a ptr[in, fmt[dec, r0]]) +foo$fmt5(a ptr[in, struct$fmt0]) + +struct$fmt0 { + f0 fmt[dec, int8] +} diff --git a/pkg/compiler/testdata/errors.txt b/pkg/compiler/testdata/errors.txt index 750916a83..cbfebbfd7 100644 --- a/pkg/compiler/testdata/errors.txt +++ b/pkg/compiler/testdata/errors.txt @@ -318,3 +318,15 @@ foo$203(a type0[42]) ### type type0 is not a template foo$204(a ptr[in, templ_struct0[42, int8]]) foo$205(a ptr[in, templ_struct0[int8, int8]]) foo$207(a ptr[in, templ_struct2[1]]) + +# fmt + +foo$fmt0(a fmt) ### wrong number of arguments for type fmt, expect format, value +foo$fmt1(a fmt[dec, int8]) ### fmt can't be syscall argument +foo$fmt2(a ptr[in, fmt[dec, ptr[in, int8]]]) ### bad fmt value ptr, expect an integer +foo$fmt3(a ptr[in, fmt[foo, int8]]) ### unexpected value foo for format argument of fmt type, expect [dec hex oct] +foo$fmt4(a ptr[in, fmt[dec, int8:3]]) ### unexpected ':', only struct fields can be bitfields + +struct$fmt0 { + f0 fmt[dec, int8:3] ### unexpected ':', only struct fields can be bitfields +} diff --git a/pkg/compiler/types.go b/pkg/compiler/types.go index c41ce4bdf..156b1505a 100644 --- a/pkg/compiler/types.go +++ b/pkg/compiler/types.go @@ -46,8 +46,9 @@ type typeArg struct { } type namedArg struct { - Name string - Type *typeArg + Name string + Type *typeArg + IsArg bool // does not need base type } const ( @@ -67,7 +68,7 @@ var typeInt = &typeDesc{ AllowColon: true, ResourceBase: true, OptArgs: 1, - Args: []namedArg{{"range", typeArgRange}}, + Args: []namedArg{{Name: "range", Type: typeArgRange}}, Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { typeArgBase.Type.Check(comp, t) }, @@ -91,7 +92,7 @@ var typePtr = &typeDesc{ Names: []string{"ptr", "ptr64"}, CanBeArgRet: canBeArg, CanBeTypedef: true, - Args: []namedArg{{"direction", typeArgDir}, {"type", typeArgType}}, + Args: []namedArg{{Name: "direction", Type: typeArgDir}, {Name: "type", Type: typeArgType}}, Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { base.ArgDir = prog.DirIn // pointers are always in base.TypeSize = comp.ptrSize @@ -127,7 +128,7 @@ var typeArray = &typeDesc{ CanBeTypedef: true, CantBeOpt: true, OptArgs: 1, - Args: []namedArg{{"type", typeArgType}, {"size", typeArgRange}}, + Args: []namedArg{{Name: "type", Type: typeArgType}, {Name: "size", Type: typeArgRange}}, CheckConsts: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { if len(args) > 1 && args[1].Value == 0 && args[1].Value2 == 0 { comp.error(args[1].Pos, "arrays of size 0 are not supported") @@ -187,7 +188,7 @@ var typeLen = &typeDesc{ CanBeArgRet: canBeArg, CantBeOpt: true, NeedBase: true, - Args: []namedArg{{"len target", typeArgLenTarget}}, + Args: []namedArg{{Name: "len target", Type: typeArgLenTarget}}, Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { var bitSize uint64 switch t.Ident { @@ -213,7 +214,7 @@ var typeConst = &typeDesc{ CanBeTypedef: true, CantBeOpt: true, NeedBase: true, - Args: []namedArg{{"value", typeArgInt}}, + Args: []namedArg{{Name: "value", Type: typeArgInt}}, Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { return &prog.ConstType{ IntTypeCommon: base, @@ -232,7 +233,7 @@ var typeFlags = &typeDesc{ CanBeTypedef: true, CantBeOpt: true, NeedBase: true, - Args: []namedArg{{"flags", typeArgFlags}}, + Args: []namedArg{{Name: "flags", Type: typeArgFlags}}, Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { name := args[0].Ident base.TypeName = name @@ -289,7 +290,7 @@ var typeVMA = &typeDesc{ Names: []string{"vma"}, CanBeArgRet: canBeArg, OptArgs: 1, - Args: []namedArg{{"size range", typeArgRange}}, + Args: []namedArg{{Name: "size range", Type: typeArgRange}}, Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { begin, end := uint64(0), uint64(0) if len(args) > 0 { @@ -309,7 +310,11 @@ var typeCsum = &typeDesc{ NeedBase: true, CantBeOpt: true, OptArgs: 1, - Args: []namedArg{{"csum target", typeArgLenTarget}, {"kind", typeArgCsumType}, {"proto", typeArgInt}}, + Args: []namedArg{ + {Name: "csum target", Type: typeArgLenTarget}, + {Name: "kind", Type: typeArgCsumType}, + {Name: "proto", Type: typeArgInt}, + }, Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { if len(args) > 2 && genCsumKind(args[1]) != prog.CsumPseudo { comp.error(args[2].Pos, "only pseudo csum can have proto") @@ -350,7 +355,10 @@ var typeProc = &typeDesc{ CanBeArgRet: canBeArg, CanBeTypedef: true, NeedBase: true, - Args: []namedArg{{"range start", typeArgInt}, {"per-proc values", typeArgInt}}, + Args: []namedArg{ + {Name: "range start", Type: typeArgInt}, + {Name: "per-proc values", Type: typeArgInt}, + }, CheckConsts: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { start := args[0].Value perProc := args[1].Value @@ -381,7 +389,7 @@ var typeProc = &typeDesc{ var typeText = &typeDesc{ Names: []string{"text"}, CantBeOpt: true, - Args: []namedArg{{"kind", typeArgTextType}}, + Args: []namedArg{{Name: "kind", Type: typeArgTextType}}, Varlen: func(comp *compiler, t *ast.Type, args []*ast.Type) bool { return true }, @@ -420,7 +428,7 @@ func genTextType(t *ast.Type) prog.TextKind { var typeBuffer = &typeDesc{ Names: []string{"buffer"}, CanBeArgRet: canBeArg, - Args: []namedArg{{"direction", typeArgDir}}, + Args: []namedArg{{Name: "direction", Type: typeArgDir}}, Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { base.TypeSize = comp.ptrSize common := genCommon("", "", 0, genDir(args[0]), false) @@ -444,7 +452,10 @@ var typeString = &typeDesc{ Names: []string{"string", stringnoz}, CanBeTypedef: true, OptArgs: 2, - Args: []namedArg{{"literal or flags", typeArgStringFlags}, {"size", typeArgInt}}, + Args: []namedArg{ + {Name: "literal or flags", Type: typeArgStringFlags}, + {Name: "size", Type: typeArgInt}, + }, Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { if t.Ident == stringnoz && len(args) > 1 { comp.error(args[0].Pos, "fixed-size string can't be non-zero-terminated") @@ -570,6 +581,66 @@ var typeArgStringFlags = &typeArg{ }, } +var typeFmt = &typeDesc{ + Names: []string{"fmt"}, + CanBeTypedef: true, + CantBeOpt: true, + Args: []namedArg{ + {Name: "format", Type: typeFmtFormat}, + {Name: "value", Type: typeArgType, IsArg: true}, + }, + Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { + desc, _, _ := comp.getArgsBase(args[1], "", base.TypeCommon.ArgDir, true) + switch desc { + case typeResource, typeInt, typeLen, typeFlags, typeProc: + default: + comp.error(t.Pos, "bad fmt value %v, expect an integer", args[1].Ident) + return + } + }, + Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { + var format prog.BinaryFormat + var size uint64 + switch args[0].Ident { + case "dec": + format = prog.FormatStrDec + size = 20 + case "hex": + format = prog.FormatStrHex + size = 18 + case "oct": + format = prog.FormatStrOct + size = 23 + } + typ := comp.genType(args[1], "", base.TypeCommon.ArgDir, true) + switch t := typ.(type) { + case *prog.ResourceType: + t.ArgFormat = format + t.TypeSize = size + case *prog.IntType: + t.ArgFormat = format + t.TypeSize = size + case *prog.LenType: + t.ArgFormat = format + t.TypeSize = size + case *prog.FlagsType: + t.ArgFormat = format + t.TypeSize = size + case *prog.ProcType: + t.ArgFormat = format + t.TypeSize = size + default: + panic(fmt.Sprintf("unexpected type: %#v", typ)) + } + return typ + }, +} + +var typeFmtFormat = &typeArg{ + Names: []string{"dec", "hex", "oct"}, + Kind: kindIdent, +} + // typeArgType is used as placeholder for any type (e.g. ptr target type). var typeArgType = &typeArg{} @@ -588,9 +659,11 @@ func init() { baseType = r.Base r = comp.resources[r.Base.Ident] } - base.TypeSize = comp.genType(baseType, "", prog.DirIn, false).Size() + baseProgType := comp.genType(baseType, "", prog.DirIn, false) + base.TypeSize = baseProgType.Size() return &prog.ResourceType{ TypeCommon: base.TypeCommon, + ArgFormat: baseProgType.Format(), } } } @@ -778,6 +851,7 @@ func init() { typeText, typeBuffer, typeString, + typeFmt, } for _, desc := range builtins { for _, name := range desc.Names { diff --git a/pkg/csource/csource.go b/pkg/csource/csource.go index 992e5ac4f..e7aa2d7f2 100644 --- a/pkg/csource/csource.go +++ b/pkg/csource/csource.go @@ -288,33 +288,7 @@ func (ctx *context) generateCalls(p prog.ExecProg) ([]string, []uint64) { w := new(bytes.Buffer) // Copyin. for _, copyin := range call.Copyin { - switch arg := copyin.Arg.(type) { - case prog.ExecArgConst: - if arg.BitfieldOffset == 0 && arg.BitfieldLength == 0 { - fmt.Fprintf(w, "\tNONFAILING(*(uint%v_t*)0x%x = %v);\n", - arg.Size*8, copyin.Addr, ctx.constArgToStr(arg)) - } else { - fmt.Fprintf(w, "\tNONFAILING(STORE_BY_BITMASK(uint%v_t, 0x%x, %v, %v, %v));\n", - arg.Size*8, copyin.Addr, ctx.constArgToStr(arg), - arg.BitfieldOffset, arg.BitfieldLength) - } - case prog.ExecArgResult: - fmt.Fprintf(w, "\tNONFAILING(*(uint%v_t*)0x%x = %v);\n", - arg.Size*8, copyin.Addr, ctx.resultArgToStr(arg)) - case prog.ExecArgData: - fmt.Fprintf(w, "\tNONFAILING(memcpy((void*)0x%x, \"%s\", %v));\n", - copyin.Addr, toCString(arg.Data), len(arg.Data)) - case prog.ExecArgCsum: - switch arg.Kind { - case prog.ExecArgCsumInet: - csumSeq++ - ctx.generateCsumInet(w, copyin.Addr, arg, csumSeq) - default: - panic(fmt.Sprintf("unknown csum kind %v", arg.Kind)) - } - default: - panic(fmt.Sprintf("bad argument type: %+v", arg)) - } + ctx.copyin(w, &csumSeq, copyin) } // Call itself. @@ -353,8 +327,14 @@ func (ctx *context) generateCalls(p prog.ExecProg) ([]string, []uint64) { } switch arg := arg.(type) { case prog.ExecArgConst: + if arg.Format != prog.FormatNative && arg.Format != prog.FormatBigEndian { + panic("sring format in syscall argument") + } fmt.Fprintf(w, "%v", ctx.constArgToStr(arg)) case prog.ExecArgResult: + if arg.Format != prog.FormatNative && arg.Format != prog.FormatBigEndian { + panic("sring format in syscall argument") + } fmt.Fprintf(w, "%v", ctx.resultArgToStr(arg)) default: panic(fmt.Sprintf("unknown arg type: %+v", arg)) @@ -419,6 +399,61 @@ func (ctx *context) generateCsumInet(w *bytes.Buffer, addr uint64, arg prog.Exec addr, csumSeq) } +func (ctx *context) copyin(w *bytes.Buffer, csumSeq *int, copyin prog.ExecCopyin) { + switch arg := copyin.Arg.(type) { + case prog.ExecArgConst: + if arg.BitfieldOffset == 0 && arg.BitfieldLength == 0 { + ctx.copyinVal(w, copyin.Addr, arg.Size, ctx.constArgToStr(arg), arg.Format) + } else { + if arg.Format != prog.FormatNative && arg.Format != prog.FormatBigEndian { + panic("bitfield+string format") + } + fmt.Fprintf(w, "\tNONFAILING(STORE_BY_BITMASK(uint%v_t, 0x%x, %v, %v, %v));\n", + arg.Size*8, copyin.Addr, ctx.constArgToStr(arg), + arg.BitfieldOffset, arg.BitfieldLength) + } + case prog.ExecArgResult: + ctx.copyinVal(w, copyin.Addr, arg.Size, ctx.resultArgToStr(arg), arg.Format) + case prog.ExecArgData: + fmt.Fprintf(w, "\tNONFAILING(memcpy((void*)0x%x, \"%s\", %v));\n", + copyin.Addr, toCString(arg.Data), len(arg.Data)) + case prog.ExecArgCsum: + switch arg.Kind { + case prog.ExecArgCsumInet: + *csumSeq++ + ctx.generateCsumInet(w, copyin.Addr, arg, *csumSeq) + default: + panic(fmt.Sprintf("unknown csum kind %v", arg.Kind)) + } + default: + panic(fmt.Sprintf("bad argument type: %+v", arg)) + } +} + +func (ctx *context) copyinVal(w *bytes.Buffer, addr, size uint64, val string, bf prog.BinaryFormat) { + switch bf { + case prog.FormatNative, prog.FormatBigEndian: + fmt.Fprintf(w, "\tNONFAILING(*(uint%v_t*)0x%x = %v);\n", size*8, addr, val) + case prog.FormatStrDec: + if size != 20 { + panic("bad strdec size") + } + fmt.Fprintf(w, "\tNONFAILING(sprintf((char*)0x%x, \"%%020llu\", (long long)%v));\n", addr, val) + case prog.FormatStrHex: + if size != 18 { + panic("bad strdec size") + } + fmt.Fprintf(w, "\tNONFAILING(sprintf((char*)0x%x, \"0x%%016llx\", (long long)%v));\n", addr, val) + case prog.FormatStrOct: + if size != 23 { + panic("bad strdec size") + } + fmt.Fprintf(w, "\tNONFAILING(sprintf((char*)0x%x, \"%%023llo\", (long long)%v));\n", addr, val) + default: + panic("unknown binary format") + } +} + func (ctx *context) constArgToStr(arg prog.ExecArgConst) string { mask := (uint64(1) << (arg.Size * 8)) - 1 v := arg.Value & mask @@ -431,7 +466,7 @@ func (ctx *context) constArgToStr(arg prog.ExecArgConst) string { if ctx.opts.Procs > 1 && arg.PidStride != 0 { val += fmt.Sprintf(" + procid*%v", arg.PidStride) } - if arg.BigEndian { + if arg.Format == prog.FormatBigEndian { val = fmt.Sprintf("htobe%v(%v)", arg.Size*8, val) } return val @@ -445,6 +480,9 @@ func (ctx *context) resultArgToStr(arg prog.ExecArgResult) string { if arg.AddOp != 0 { res = fmt.Sprintf("%v+%v", res, arg.AddOp) } + if arg.Format == prog.FormatBigEndian { + res = fmt.Sprintf("htobe%v(%v)", arg.Size*8, res) + } return res } diff --git a/pkg/csource/linux_common.go b/pkg/csource/linux_common.go index c1313c018..b2431c4fe 100644 --- a/pkg/csource/linux_common.go +++ b/pkg/csource/linux_common.go @@ -10,6 +10,7 @@ var commonHeaderLinux = ` #endif #include +#include #include #include #if defined(SYZ_EXECUTOR) || defined(SYZ_THREADED) || defined(SYZ_COLLIDE) @@ -21,7 +22,6 @@ var commonHeaderLinux = ` #include #include #include -#include #include #include #include @@ -39,7 +39,6 @@ var commonHeaderLinux = ` #include #include #include -#include #include #include #include @@ -72,7 +71,6 @@ var commonHeaderLinux = ` #include #include #include -#include #include #include #include @@ -88,24 +86,20 @@ var commonHeaderLinux = ` #include #include #include -#include #include #endif #if defined(SYZ_EXECUTOR) || defined(__NR_syz_open_dev) || defined(__NR_syz_open_procfs) #include -#include #include #include #endif #if defined(SYZ_EXECUTOR) || defined(__NR_syz_fuse_mount) || defined(__NR_syz_fuseblk_mount) #include -#include #include #include #endif #if defined(SYZ_EXECUTOR) || defined(__NR_syz_open_pts) #include -#include #include #include #endif @@ -115,7 +109,6 @@ var commonHeaderLinux = ` #include #include #include -#include #include #include #endif @@ -140,7 +133,6 @@ var commonHeaderLinux = ` #include #include #include -#include #include #include #include -- cgit mrf-deployment