3
3
package kfuzztest
4
4
5
5
import (
6
+ "debug/dwarf"
6
7
"fmt"
7
- "regexp"
8
8
"strings"
9
9
10
10
"github.com/google/syzkaller/pkg/ast"
@@ -53,7 +53,11 @@ func (b *Builder) EmitSyzlangDescription() (string, error) {
53
53
var descBuilder strings.Builder
54
54
descBuilder .WriteString ("# This description was automatically generated with tools/kfuzztest-gen\n " )
55
55
for _ , s := range b .structs {
56
- descBuilder .WriteString (syzStructToSyzlang (s , constraintMap , annotationMap ))
56
+ structDesc , err := syzStructToSyzlang (s , constraintMap , annotationMap )
57
+ if err != nil {
58
+ return "" , err
59
+ }
60
+ descBuilder .WriteString (structDesc )
57
61
descBuilder .WriteString ("\n \n " )
58
62
}
59
63
@@ -64,9 +68,7 @@ func (b *Builder) EmitSyzlangDescription() (string, error) {
64
68
}
65
69
}
66
70
67
- fmt .Println (descBuilder .String ())
68
-
69
- // Format the output syzlang descriptions.
71
+ // Format the output syzlang descriptions for consistency.
70
72
var astError error
71
73
eh := func (pos ast.Pos , msg string ) {
72
74
astError = fmt .Errorf ("Failure: %v: %v\n " , pos , msg )
@@ -76,135 +78,181 @@ func (b *Builder) EmitSyzlangDescription() (string, error) {
76
78
return "" , astError
77
79
}
78
80
if descAst == nil {
79
- return "" , fmt .Errorf ("Failed to format generated syzlang. Is it well-formed?" )
81
+ return "" , fmt .Errorf ("failed to format generated syzkaller description - is it well-formed?" )
80
82
}
81
-
82
83
return string (ast .Format (descAst )), nil
83
84
}
84
85
85
- // FIXME: this function is gross because of the weird logic cases that arises
86
- // from having annotations that determine the type. I'm sure there's a much
87
- // nicer way of writing this control flow.
88
86
func syzStructToSyzlang (s SyzStruct , constraintMap map [string ]map [string ]SyzConstraint ,
89
- annotationMap map [string ]map [string ]SyzAnnotation ) string {
90
- out := fmt .Sprintf ("%s {\n " , s .Name )
91
- for _ , field := range s .Fields {
92
- out += "\t "
93
- typeName := dwarfToSyzlangType (field .TypeName )
94
-
95
- aSubMap , ok := annotationMap ["struct " + s .Name ]
96
- if ok {
97
- annotation , ok := aSubMap [field .Name ]
98
- if ! ok {
99
- goto append_type
100
- }
101
-
102
- // Annotated fields require special handling.
103
- switch annotation .Attribute {
104
- case AttributeLen :
105
- out += fmt .Sprintf ("%s\t len[%s, %s]" , field .Name , annotation .LinkedFieldName , typeName )
106
- case AttributeString :
107
- out += fmt .Sprintf ("%s\t ptr[in, string]" , field .Name )
108
- case AttributeArray :
109
- // An array type is prefixed with a leading "*", which we remove
110
- // to resolve the underlying type.
111
- arrayType := typeName [1 :]
112
- out += fmt .Sprintf ("%s\t ptr[in, array[%s]]" , field .Name , arrayType )
113
- }
114
- out += "\n "
115
- continue
116
- }
87
+ annotationMap map [string ]map [string ]SyzAnnotation ) (string , error ) {
88
+ var builder strings.Builder
117
89
118
- // just appends the type as it appear in the
119
- append_type:
120
- out += fmt .Sprintf ("%s\t %s" , field .Name , typeName )
121
-
122
- subMap , ok := constraintMap ["struct " + s .Name ]
123
- if ok {
124
- constraint , ok := subMap [field .Name ]
125
- if ok {
126
- out += syzConstraintToSyzlang (constraint )
127
- }
90
+ fmt .Fprintf (& builder , "%s {\n " , s .Name )
91
+ structAnnotations := annotationMap ["struct " + s .Name ]
92
+ structConstraints := constraintMap ["struct " + s .Name ]
93
+ for _ , field := range s .Fields {
94
+ line , err := syzFieldToSyzLang (field , structConstraints , structAnnotations )
95
+ if err != nil {
96
+ return "" , err
128
97
}
129
- out += " \n "
98
+ fmt . Fprintf ( & builder , " \t %s \n " , line )
130
99
}
131
- out += "}"
132
- return out
100
+ fmt . Fprint ( & builder , "}" )
101
+ return builder . String (), nil
133
102
}
134
103
135
- func syzFuncToSyzlang (s SyzFunc ) string {
136
- typeName := strings .TrimPrefix (s .InputStructName , "struct " )
104
+ func syzFieldToSyzLang (field SyzField , constraintMap map [string ]SyzConstraint ,
105
+ annotationMap map [string ]SyzAnnotation ) (string , error ) {
106
+ constraint , hasConstraint := constraintMap [field .Name ]
107
+ annotation , hasAnnotation := annotationMap [field .Name ]
137
108
138
- out := fmt .Sprintf ("syz_kfuzztest_run$%s(" , s .Name )
139
- out += fmt .Sprintf ("name ptr[in, string[\" %s\" ]], " , s .Name )
140
- out += fmt .Sprintf ("data ptr[in, %s], " , typeName )
141
- out += "len bytesize[data])"
142
- // TODO:(ethangraham) The only other way I can think of getting this name
143
- // would involve using the "reflect" package and matching against the
144
- // KFuzzTest name, which isn't much better than hardcoding this.
145
- out += "(kfuzz_test)"
109
+ var typeDesc string
110
+ var err error
111
+ if hasAnnotation {
112
+ // Annotations override the existing type definitions.
113
+ typeDesc , err = processAnnotation (field , annotation )
114
+ } else {
115
+ typeDesc , err = dwarfToSyzlangType (field .dwarfType , false )
116
+ }
117
+ if err != nil {
118
+ return "" , err
119
+ }
146
120
147
- return out
121
+ if hasConstraint {
122
+ constraint , err := processConstraint (constraint )
123
+ if err != nil {
124
+ return "" , err
125
+ }
126
+ typeDesc += constraint
127
+ }
128
+ return fmt .Sprintf ("%s %s" , field .Name , typeDesc ), nil
148
129
}
149
130
150
- func syzConstraintToSyzlang (c SyzConstraint ) string {
131
+ func processConstraint (c SyzConstraint ) ( string , error ) {
151
132
switch c .ConstraintType {
152
133
case ExpectEq :
153
- return fmt .Sprintf ("[%d]" , c .Value1 )
134
+ return fmt .Sprintf ("[%d]" , c .Value1 ), nil
135
+ case ExpectNe :
136
+ // syzkaller does not have a built-in way to support an inequality
137
+ // constraint AFAIK.
138
+ return "" , nil
154
139
case ExpectLt :
155
- return fmt .Sprintf ("[0:%d]" , c .Value1 - 1 )
140
+ return fmt .Sprintf ("[0:%d]" , c .Value1 - 1 ), nil
156
141
case ExpectLe :
157
- return fmt .Sprintf ("[0:%d]" , c .Value1 )
142
+ return fmt .Sprintf ("[0:%d]" , c .Value1 ), nil
158
143
case ExpectGt :
159
- return fmt .Sprintf ("[%d]" , c .Value1 + 1 )
144
+ return fmt .Sprintf ("[%d]" , c .Value1 + 1 ), nil
160
145
case ExpectGe :
161
- return fmt .Sprintf ("[%d]" , c .Value1 )
146
+ return fmt .Sprintf ("[%d]" , c .Value1 ), nil
162
147
case ExpectInRange :
163
- return fmt .Sprintf ("[%d:%d]" , c .Value1 , c .Value2 )
148
+ return fmt .Sprintf ("[%d:%d]" , c .Value1 , c .Value2 ), nil
164
149
default :
165
- return ""
150
+ fmt .Printf ("c = %d\n " , c .ConstraintType )
151
+ return "" , fmt .Errorf ("unsupported constraint type" )
166
152
}
167
153
}
168
154
169
- func isArray (typeName string ) (bool , string ) {
170
- re := regexp .MustCompile (`^\[(\d+)\]([a-zA-Z]+)$` )
171
- matches := re .FindStringSubmatch (typeName )
172
- if len (matches ) == 0 {
173
- return false , ""
155
+ func processAnnotation (field SyzField , annotation SyzAnnotation ) (string , error ) {
156
+ switch annotation .Attribute {
157
+ case AttributeLen :
158
+ underlyingType , err := dwarfToSyzlangType (field .dwarfType , false )
159
+ if err != nil {
160
+ return "" , err
161
+ }
162
+ return fmt .Sprintf ("len[%s, %s]" , annotation .LinkedFieldName , underlyingType ), nil
163
+ case AttributeString :
164
+ return "ptr[in, string]" , nil
165
+ case AttributeArray :
166
+ pointeeType , isPtr := resolvesToPtr (field .dwarfType )
167
+ if ! isPtr {
168
+ return "" , fmt .Errorf ("can only annotate pointer fields are arrays" )
169
+ }
170
+ // TODO: discards const qualifier.
171
+ typeDesc , err := dwarfToSyzlangType (pointeeType , false )
172
+ if err != nil {
173
+ return "" , err
174
+ }
175
+ return fmt .Sprintf ("ptr[in, array[%s]]" , typeDesc ), nil
176
+ default :
177
+ return "" , fmt .Errorf ("unsupported attribute type" )
174
178
}
175
- return true , fmt .Sprintf ("array[%s, %s]" , dwarfToSyzlangType (matches [2 ]), matches [1 ])
176
179
}
177
180
178
- func dwarfToSyzlangType (typeName string ) string {
179
- if after , ok := strings .CutPrefix (typeName , "struct " ); ok {
180
- return after
181
+ // Returns true iff `dwarfType` resolved down to a pointer. For example,
182
+ // a `const *void` which isn't directly a pointer.
183
+ func resolvesToPtr (dwarfType dwarf.Type ) (dwarf.Type , bool ) {
184
+ switch t := dwarfType .(type ) {
185
+ case * dwarf.QualType :
186
+ return resolvesToPtr (t .Type )
187
+ case * dwarf.PtrType :
188
+ return t .Type , true
181
189
}
190
+ return nil , false
191
+ }
182
192
183
- if after , ok := strings .CutPrefix (typeName , "*const struct" ); ok {
184
- return fmt .Sprintf ("ptr[in, %s]" , after )
185
- } else if after , ok := strings .CutPrefix (typeName , "*struct" ); ok {
186
- return fmt .Sprintf ("ptr[inout, %s]" , after )
187
- }
193
+ func syzFuncToSyzlang (s SyzFunc ) string {
194
+ var builder strings.Builder
195
+ typeName := strings .TrimPrefix (s .InputStructName , "struct " )
188
196
189
- isArr , arr := isArray (typeName )
190
- if isArr {
191
- return arr
192
- }
197
+ fmt .Fprintf (& builder , "syz_kfuzztest_run$%s(" , s .Name )
198
+ fmt .Fprintf (& builder , "name ptr[in, string[\" %s\" ]], " , s .Name )
199
+ fmt .Fprintf (& builder , "data ptr[in, %s], " , typeName )
200
+ builder .WriteString ("len bytesize[data])" )
201
+ // TODO:(ethangraham) The only other way I can think of getting this name
202
+ // would involve using the "reflect" package and matching against the
203
+ // KFuzzTest name, which isn't much better than hardcoding this.
204
+ builder .WriteString ("(kfuzz_test)" )
205
+ return builder .String ()
206
+ }
193
207
194
- switch typeName {
195
- case "long unsigned int" , "long int" , "size_t" :
196
- return "int64"
197
- case "int" , "unsigned int" :
198
- return "int32"
199
- case "char" :
200
- return "int8"
201
- case "__u16" :
202
- return "int16"
203
- case "*const char" , "*const void" , "*const unsigned char" :
204
- return "ptr[in, array[int8]]" // const pointers are read-only
205
- case "*char" , "*void" :
206
- return "ptr[inout, array[int8]]"
208
+ // Given a dwarf type, returns a syzlang string representation of this type.
209
+ func dwarfToSyzlangType (dwarfType dwarf.Type , isConst bool ) (string , error ) {
210
+ var dir string
211
+ if isConst {
212
+ dir = "in"
213
+ } else {
214
+ dir = "inout"
215
+ }
216
+ switch t := dwarfType .(type ) {
217
+ case * dwarf.PtrType :
218
+ underlyingType , err := dwarfToSyzlangType (t .Type , false )
219
+ if err != nil {
220
+ return "" , err
221
+ }
222
+ return fmt .Sprintf ("ptr[%s, %s]" , dir , underlyingType ), nil
223
+ case * dwarf.QualType :
224
+ if t .Qual == "const" {
225
+ return dwarfToSyzlangType (t .Type , true )
226
+ } else {
227
+ return "" , fmt .Errorf ("no support for %s qualifier" , t .Qual )
228
+ }
229
+ case * dwarf.ArrayType :
230
+ underlyingType , err := dwarfToSyzlangType (t .Type , false )
231
+ if err != nil {
232
+ return "" , err
233
+ }
234
+ // If t.Count == -1 then this is a varlen array as per debug/dwarf
235
+ // documentation.
236
+ if t .Count == - 1 {
237
+ return fmt .Sprintf ("array[%s]" , underlyingType ), nil
238
+ } else {
239
+ return fmt .Sprintf ("array[%s, %d]" , underlyingType , t .Count ), nil
240
+ }
241
+ case * dwarf.TypedefType :
242
+ return dwarfToSyzlangType (t .Type , isConst )
243
+ case * dwarf.IntType , * dwarf.UintType :
244
+ numBits := t .Size () * 8
245
+ return fmt .Sprintf ("int%d" , numBits ), nil
246
+ case * dwarf.CharType , * dwarf.UcharType :
247
+ return "int8" , nil
248
+ // `void` isn't a valid type by itself, so we know that it would have
249
+ // been wrapped in a pointer, e.g., `void *`. For this reason, we can return
250
+ // just interpret it as a byte, i.e., int8.
251
+ case * dwarf.VoidType :
252
+ return "int8" , nil
253
+ case * dwarf.StructType :
254
+ return strings .TrimPrefix (t .StructName , "struct " ), nil
207
255
default :
208
- return typeName
256
+ return "" , fmt . Errorf ( "unsupported type %s" , dwarfType . String ())
209
257
}
210
258
}
0 commit comments