From 72afa07d28afe7f81a9874f9389332df09afb336 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Tue, 27 Aug 2024 10:28:30 +0200 Subject: [PATCH] new: can.fuzz now supports an optional size argument (thanks musafir) --- modules/can/can.go | 7 ++-- modules/can/can_dbc.go | 8 ++++ modules/can/can_fuzz.go | 81 ++++++++++++++++++++++++++--------------- 3 files changed, 63 insertions(+), 33 deletions(-) diff --git a/modules/can/can.go b/modules/can/can.go index 8ee30041..5f447174 100644 --- a/modules/can/can.go +++ b/modules/can/can.go @@ -13,10 +13,10 @@ import ( type CANModule struct { session.SessionModule + transport string deviceName string dumpName string dumpInject bool - transport string filter string filterExpr *bexpr.Evaluator dbc *DBC @@ -101,13 +101,14 @@ func NewCanModule(s *session.Session) *CANModule { return mod.Inject(args[0]) })) - mod.AddHandler(session.NewModuleHandler("can.fuzz ID_OR_NODE_NAME", `(?i)^can\.fuzz\s+(.+)$`, + mod.AddHandler(session.NewModuleHandler("can.fuzz ID_OR_NODE_NAME OPTIONAL_SIZE", `(?i)^can\.fuzz\s+([^\s]+)\s*(\d*)$`, "If an hexadecimal frame ID is specified, create a randomized version of it and inject it. If a node name is specified, a random message for the given node will be instead used.", func(args []string) error { if !mod.Running() { return errors.New("can module not running") } - return mod.Fuzz(args[0]) + + return mod.Fuzz(args[0], args[1]) })) return mod diff --git a/modules/can/can_dbc.go b/modules/can/can_dbc.go index 62cda4e7..36db7a62 100644 --- a/modules/can/can_dbc.go +++ b/modules/can/can_dbc.go @@ -147,6 +147,14 @@ func (dbc *DBC) Messages() []*descriptor.Message { return dbc.db.Messages } +func (dbc *DBC) AvailableMessages() []string { + avail := []string{} + for _, msg := range dbc.Messages() { + avail = append(avail, fmt.Sprintf("%d (%s)", msg.ID, msg.Name)) + } + return avail +} + func (dbc *DBC) Senders() []string { dbc.RLock() defer dbc.RUnlock() diff --git a/modules/can/can_fuzz.go b/modules/can/can_fuzz.go index ba557da0..d47720d0 100644 --- a/modules/can/can_fuzz.go +++ b/modules/can/can_fuzz.go @@ -12,22 +12,15 @@ import ( "go.einride.tech/can" ) -func (mod *CANModule) Fuzz(id string) error { - rncSource := rand.NewSource(time.Now().Unix()) - rng := rand.New(rncSource) - +func (mod *CANModule) fuzzSelectFrame(id string, rng *rand.Rand) (uint64, error) { // let's try as an hex number first - // frameID, err := strconv.Atoi(id) frameID, err := strconv.ParseUint(id, 16, 32) - dataLen := 0 - frameData := ([]byte)(nil) - if err != nil { - if mod.dbc != nil { - // not a number, use as node name + // not a number, use as node name if we have a dbc + if mod.dbc.Loaded() { fromSender := mod.dbc.MessagesBySender(id) if len(fromSender) == 0 { - return fmt.Errorf("no messages defined in DBC file for node %s, available nodes: %s", id, mod.dbc.Senders()) + return 0, fmt.Errorf("no messages defined in DBC file for node %s, available nodes: %s", id, mod.dbc.Senders()) } idx := rng.Intn(len(fromSender)) @@ -35,35 +28,41 @@ func (mod *CANModule) Fuzz(id string) error { mod.Info("selected %s > (%d) %s", id, selected.ID, selected.Name) frameID = uint64(selected.ID) } else { - return err + // no dbc, just return the error + return 0, err } } + return frameID, nil +} + +func (mod *CANModule) fuzzGenerateFrame(frameID uint64, size int, rng *rand.Rand) (*can.Frame, error) { + dataLen := 0 + frameData := ([]byte)(nil) // if we have a DBC if mod.dbc.Loaded() { if message := mod.dbc.MessageById(uint32(frameID)); message != nil { - mod.Info("found as %s", message.Name) - + mod.Info("using message %s", message.Name) dataLen = int(message.Length) frameData = make([]byte, dataLen) if _, err := rand.Read(frameData); err != nil { - return err + return nil, err } } else { - avail := []string{} - for _, msg := range mod.dbc.Messages() { - avail = append(avail, fmt.Sprintf("%d (%s)", msg.ID, msg.Name)) - } - return fmt.Errorf("message with id %d not found in DBC file, available ids: %v", frameID, strings.Join(avail, ", ")) + return nil, fmt.Errorf("message with id %d not found in DBC file, available ids: %v", frameID, strings.Join(mod.dbc.AvailableMessages(), ", ")) } } else { - dataLen = rng.Intn(int(can.MaxDataLength)) - frameData = make([]byte, dataLen) - - if _, err := rand.Read(frameData); err != nil { - return err + if size <= 0 { + // pick randomly + dataLen = rng.Intn(int(can.MaxDataLength)) + } else { + // user selected + dataLen = size + } + frameData = make([]byte, dataLen) + if _, err := rand.Read(frameData); err != nil { + return nil, err } - mod.Warning("no dbc loaded, creating frame with %d bytes of random data", dataLen) } @@ -76,12 +75,34 @@ func (mod *CANModule) Fuzz(id string) error { copy(frame.Data[:], frameData) - mod.Info("injecting %s of CAN frame %d ...", - humanize.Bytes(uint64(frame.Length)), frame.ID) + return &frame, nil +} - if err := mod.send.TransmitFrame(context.Background(), frame); err != nil { - return err +func (mod *CANModule) Fuzz(id string, optSize string) error { + rncSource := rand.NewSource(time.Now().Unix()) + rng := rand.New(rncSource) + + fuzzSize := 0 + if optSize != "" { + if num, err := strconv.Atoi(optSize); err != nil { + return fmt.Errorf("could not parse numeric size from '%s': %v", optSize, err) + } else if num > 8 { + return fmt.Errorf("max can frame size is 8, %d given", num) + } else { + fuzzSize = num + } } + if frameID, err := mod.fuzzSelectFrame(id, rng); err != nil { + return err + } else if frame, err := mod.fuzzGenerateFrame(frameID, fuzzSize, rng); err != nil { + return err + } else { + mod.Info("injecting %s of CAN frame %d ...", + humanize.Bytes(uint64(frame.Length)), frame.ID) + if err := mod.send.TransmitFrame(context.Background(), *frame); err != nil { + return err + } + } return nil }