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  }