github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/declextract/netlink.go (about) 1 // Copyright 2024 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package declextract 5 6 import ( 7 "fmt" 8 "strings" 9 ) 10 11 func (ctx *context) serializeNetlink() { 12 // policyQueue helps to emit policies on their first use. 13 pq := &policyQueue{ 14 policies: make(map[string]*NetlinkPolicy), 15 } 16 for _, pol := range ctx.NetlinkPolicies { 17 pq.policies[pol.Name] = pol 18 } 19 for _, fam := range ctx.NetlinkFamilies { 20 if len(fam.Ops) == 0 { 21 // TODO: do something for these as well. These exist for a reason. 22 // Probably only send broadcast notifications (can bind and recvmsg). 23 continue 24 } 25 id := stringIdentifier(fam.Name) 26 ctx.fmt("resource genl_%v_family_id%v[int16]\n", id, autoSuffix) 27 ctx.fmt("type msghdr_%v%v[CMD, POLICY] msghdr_netlink[netlink_msg_t"+ 28 "[genl_%v_family_id%v, genlmsghdr_t[CMD], POLICY]]\n", id, autoSuffix, id, autoSuffix) 29 ctx.fmt("syz_genetlink_get_family_id%v_%v(name ptr[in, string[\"%v\"]],"+ 30 " fd sock_nl_generic) genl_%v_family_id%v\n\n", autoSuffix, id, fam.Name, id, autoSuffix) 31 32 for _, op := range fam.Ops { 33 policy := voidType 34 if op.Policy != "" { 35 policy = op.Policy + autoSuffix 36 pq.policyUsed(op.Policy) 37 } 38 name := ctx.uniqualize("netlink op", op.Name) 39 ctx.fmt("sendmsg%v_%v(fd sock_nl_generic,"+ 40 " msg ptr[in, msghdr_%v%v[%v, %v]], f flags[send_flags])\n", 41 autoSuffix, name, id, autoSuffix, op.Name, policy) 42 43 ctx.noteInterface(&Interface{ 44 Type: IfaceNetlinkOp, 45 Name: op.Name, 46 IdentifyingConst: op.Name, 47 Files: []string{fam.SourceFile}, 48 Func: op.Func, 49 Access: op.Access, 50 AutoDescriptions: TristateYes, 51 }) 52 } 53 54 for len(pq.pending) != 0 { 55 pol := pq.pending[0] 56 pq.pending = pq.pending[1:] 57 ctx.serializeNetlinkPolicy(pol, pq) 58 } 59 } 60 } 61 62 type policyQueue struct { 63 policies map[string]*NetlinkPolicy 64 pending []*NetlinkPolicy 65 } 66 67 func (pq *policyQueue) policyUsed(name string) { 68 if pol := pq.policies[name]; pol != nil { 69 delete(pq.policies, name) 70 pq.pending = append(pq.pending, pol) 71 } 72 } 73 74 func (ctx *context) serializeNetlinkPolicy(pol *NetlinkPolicy, pq *policyQueue) { 75 if len(pol.Attrs) == 0 { 76 ctx.fmt("type %v auto_todo\n", pol.Name+autoSuffix) 77 return 78 } 79 ctx.fmt("%v [\n", pol.Name+autoSuffix) 80 for _, attr := range pol.Attrs { 81 ctx.fmt("%v %v\n", attr.Name, ctx.nlattrType(attr, pq)) 82 } 83 ctx.fmt("] [varlen]\n") 84 } 85 86 func (ctx *context) nlattrType(attr *NetlinkAttr, pq *policyQueue) string { 87 nlattr, typ := "nlattr", "" 88 switch attr.Kind { 89 case "NLA_BITFIELD32": 90 // TODO: Extract values from NLA_POLICY_BITFIELD32 macro. 91 typ = "int32" 92 case "NLA_MSECS": 93 typ = "int64" 94 case "NLA_FLAG": 95 typ = voidType 96 case "NLA_NESTED", "NLA_NESTED_ARRAY": 97 nlattr = "nlnest" 98 policy := "nl_generic_attr" 99 if attr.NestedPolicy != "" { 100 pq.policyUsed(attr.NestedPolicy) 101 policy = attr.NestedPolicy + autoSuffix 102 } 103 typ = fmt.Sprintf("array[%v]", policy) 104 if attr.Kind == "NLA_NESTED_ARRAY" { 105 typ = fmt.Sprintf("array[nlnest[0, %v]]", typ) 106 } 107 default: 108 field := &Field{ 109 Name: strings.ToLower(attr.Name), 110 Type: ctx.netlinkType(attr), 111 } 112 typ = ctx.fieldType(field, nil, "", true) 113 } 114 return fmt.Sprintf("%v[%v, %v]", nlattr, attr.Name, typ) 115 } 116 117 func (ctx *context) netlinkType(attr *NetlinkAttr) *Type { 118 switch attr.Kind { 119 case "NLA_STRING", "NLA_NUL_STRING": 120 return &Type{ 121 Buffer: &BufferType{ 122 MaxSize: attr.MaxSize, 123 IsString: true, 124 IsNonTerminated: attr.Kind == "NLA_STRING", 125 }, 126 } 127 case "NLA_BINARY", "NLA_UNSPEC", "": 128 if attr.Elem == nil { 129 switch attr.MaxSize { 130 case 1, 2, 4, 8: 131 attr.Kind = fmt.Sprintf("NLA_U%v", attr.MaxSize*8) 132 return ctx.netlinkTypeInt(attr) 133 } 134 minSize := 0 135 if attr.Kind != "NLA_BINARY" { 136 minSize = attr.MaxSize 137 } 138 return &Type{ 139 Buffer: &BufferType{ 140 MaxSize: attr.MaxSize, 141 MinSize: minSize, 142 }, 143 } 144 } 145 elemSize := 1 146 switch { 147 case attr.Elem.Int != nil: 148 elemSize = attr.Elem.Int.ByteSize 149 case attr.Elem.Struct != "": 150 if str := ctx.structs[attr.Elem.Struct+autoSuffix]; str != nil { 151 elemSize = str.ByteSize 152 } else { 153 ctx.error("binary nlattr %v referenced non-existing struct %v", 154 attr.Name, attr.Elem.Struct) 155 } 156 default: 157 ctx.error("binary nlattr %v has unsupported elem type", attr.Name) 158 } 159 if attr.MaxSize%elemSize != 0 { 160 ctx.error("binary nlattr %v has odd size: %v, elem size %v", 161 attr.Name, attr.MaxSize, elemSize) 162 } 163 numElems := attr.MaxSize / elemSize 164 if numElems == 1 { 165 return attr.Elem 166 } 167 return &Type{ 168 Array: &ArrayType{ 169 Elem: attr.Elem, 170 MaxSize: numElems, 171 }, 172 } 173 default: 174 return ctx.netlinkTypeInt(attr) 175 } 176 } 177 178 func (ctx *context) netlinkTypeInt(attr *NetlinkAttr) *Type { 179 size, be := 0, false 180 switch attr.Kind { 181 case "NLA_U8", "NLA_S8": 182 size = 1 183 case "NLA_U16", "NLA_S16": 184 size = 2 185 case "NLA_U32", "NLA_S32": 186 size = 4 187 case "NLA_U64", "NLA_S64", "NLA_SINT", "NLA_UINT": 188 size = 8 189 case "NLA_BE16": 190 size, be = 2, true 191 case "NLA_BE32": 192 size, be = 4, true 193 default: 194 panic(fmt.Sprintf("unhandled netlink attribute kind %v", attr.Kind)) 195 } 196 return &Type{ 197 Int: &IntType{ 198 ByteSize: size, 199 isBigEndian: be, 200 }, 201 } 202 }