pkg/compiler: support non-zero-terminated strings

Add stringnoz type.
This commit is contained in:
Dmitry Vyukov 2018-01-18 18:48:39 +01:00
parent c77c36d5fa
commit 3661e26e74
7 changed files with 72 additions and 48 deletions

View File

@ -39,6 +39,9 @@ rest of the type-options are type-specific:
either a string value in quotes for constant strings (e.g. "foo"),
or a reference to string flags,
optionally followed by a buffer size (string values will be padded with \x00 to that size)
"stringnoz": a non-zero-terminated memory buffer (no pointer indirection implied), type-options:
either a string value in quotes for constant strings (e.g. "foo"),
or a reference to string flags,
"filename": a file/link/dir name, no pointer indirection implied, in most cases you want `ptr[in, filename]`
"fileoff": offset within a file
"len": length of another field (for array it is number of elements), type-options:

View File

@ -5,6 +5,17 @@ foo$0(a int8)
foo$1(a int8[C1:C2])
foo$2() ptr[out, array[int32]]
strings {
f1 string
f2 string["foo"]
f3 string["foo", 10]
f4 string[string_flags, 10]
f5 stringnoz
f6 stringnoz["foo"]
} [packed]
string_flags = "foo", "barbaz"
# Proc type.
proc_struct1 {

View File

@ -111,6 +111,8 @@ foo$53(a proc[20, 10, opt])
foo$54(a ptr[in, string["foo", C1]])
foo$55(a int8[opt[int8]]) ### opt can't have arguments
foo$56(a void) ### void can't be syscall argument
foo$57(a ptr[in, stringnoz["foo", 10]]) ### fixed-size string can't be non-zero-terminated
foo$58(a ptr[in, stringnoz[sf2, 10]]) ### fixed-size string can't be non-zero-terminated
opt { ### struct uses reserved name opt
f1 int32
@ -188,8 +190,8 @@ typestruct {
}
type type0 int8
type type0 int8 ### type type0 redeclared, previously declared as type alias at errors.txt:190:6
resource type0[int32] ### type type0 redeclared, previously declared as type alias at errors.txt:190:6
type type0 int8 ### type type0 redeclared, previously declared as type alias at errors.txt:192:6
resource type0[int32] ### type type0 redeclared, previously declared as type alias at errors.txt:192:6
type0 = 0, 1
type type1 type1 ### type instantiation loop: type1 -> type1
type type2 int8:4 ### unexpected ':', only struct fields can be bitfields

View File

@ -441,19 +441,20 @@ var typeBuffer = &typeDesc{
}
var typeString = &typeDesc{
Names: []string{"string"},
Names: []string{"string", "stringnoz"},
CanBeTypedef: true,
OptArgs: 2,
Args: []namedArg{{"literal or flags", typeArgStringFlags}, {"size", 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")
}
},
CheckConsts: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) {
if len(args) > 1 {
size := args[1].Value
vals := []string{args[0].String}
if args[0].Ident != "" {
vals = genStrArray(comp.strFlags[args[0].Ident].Values)
}
vals := comp.genStrings(t, args)
for _, s := range vals {
s += "\x00"
if uint64(len(s)) > size {
comp.error(args[0].Pos, "string value %q exceeds buffer length %v",
s, size)
@ -462,52 +463,67 @@ var typeString = &typeDesc{
}
},
Varlen: func(comp *compiler, t *ast.Type, args []*ast.Type) bool {
return comp.stringSize(args) == 0
return comp.stringSize(t, args) == 0
},
Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type {
subkind := ""
var vals []string
if len(args) > 0 {
if args[0].String != "" {
vals = append(vals, args[0].String)
} else {
subkind = args[0].Ident
vals = genStrArray(comp.strFlags[subkind].Values)
}
if len(args) > 0 && args[0].Ident != "" {
subkind = args[0].Ident
}
var size uint64
if len(args) > 1 {
size = args[1].Value
}
for i, s := range vals {
s += "\x00"
for uint64(len(s)) < size {
s += "\x00"
}
vals[i] = s
}
base.TypeSize = comp.stringSize(args)
vals := comp.genStrings(t, args)
base.TypeSize = comp.stringSize(t, args)
return &prog.BufferType{
TypeCommon: base.TypeCommon,
Kind: prog.BufferString,
SubKind: subkind,
Values: vals,
NoZ: t.Ident == "stringnoz",
}
},
}
func (comp *compiler) genStrings(t *ast.Type, args []*ast.Type) []string {
var vals []string
if len(args) > 0 {
if args[0].String != "" {
vals = append(vals, args[0].String)
} else {
vals = genStrArray(comp.strFlags[args[0].Ident].Values)
}
}
if t.Ident == "stringnoz" {
return vals
}
var size uint64
if len(args) > 1 {
size = args[1].Value
}
for i, s := range vals {
s += "\x00"
for uint64(len(s)) < size {
s += "\x00"
}
vals[i] = s
}
return vals
}
// stringSize returns static string size, or 0 if it is variable length.
func (comp *compiler) stringSize(args []*ast.Type) uint64 {
func (comp *compiler) stringSize(t *ast.Type, args []*ast.Type) uint64 {
switch len(args) {
case 0:
return 0 // a random string
case 1:
var z uint64
if t.Ident == "string" {
z = 1
}
if args[0].String != "" {
return uint64(len(args[0].String)) + 1 // string constant
return uint64(len(args[0].String)) + z // string constant
}
var size uint64
for _, s := range comp.strFlags[args[0].Ident].Values {
s1 := uint64(len(s.Value)) + 1
s1 := uint64(len(s.Value)) + z
if size != 0 && size != s1 {
return 0 // strings of different lengths
}

View File

@ -123,7 +123,7 @@ func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable, corpus []*Pro
}
a.data = mutateData(r, data, minLen, maxLen)
} else {
a.data = r.randString(s, t.Values, t.Dir())
a.data = r.randString(s, t)
}
case BufferFilename:
a.data = []byte(r.filename(s))

View File

@ -182,22 +182,13 @@ func (r *randGen) filename(s *state) string {
return files[r.Intn(len(files))]
}
func (r *randGen) randString(s *state, vals []string, dir Dir) []byte {
data := r.randStringImpl(s, vals)
if dir == DirOut {
for i := range data {
data[i] = 0
}
}
return data
}
func (r *randGen) randStringImpl(s *state, vals []string) []byte {
if len(vals) != 0 {
return []byte(vals[r.Intn(len(vals))])
func (r *randGen) randString(s *state, t *BufferType) []byte {
if len(t.Values) != 0 {
return []byte(t.Values[r.Intn(len(t.Values))])
}
if len(s.strings) != 0 && r.bin() {
// Return an existing string.
// TODO(dvyukov): make s.strings indexed by string SubKind.
strings := make([]string, 0, len(s.strings))
for s := range s.strings {
strings = append(strings, s)
@ -220,7 +211,7 @@ func (r *randGen) randStringImpl(s *state, vals []string) []byte {
buf.Write([]byte{byte(r.Intn(256))})
}
}
if !r.oneOf(100) {
if r.oneOf(100) == t.NoZ {
buf.Write([]byte{0})
}
return buf.Bytes()
@ -602,7 +593,7 @@ func (r *randGen) generateArg(s *state, typ Type) (arg Arg, calls []*Call) {
}
return MakeDataArg(a, data), nil
case BufferString:
data := r.randString(s, a.Values, a.Dir())
data := r.randString(s, a)
if a.Dir() == DirOut {
return MakeOutDataArg(a, uint64(len(data))), nil
}

View File

@ -227,6 +227,7 @@ type BufferType struct {
Text TextKind // for BufferText
SubKind string
Values []string // possible values for BufferString kind
NoZ bool // non-zero terminated BufferString
}
type ArrayKind int