Skip to content

Commit

Permalink
refactor(decl/syscalls): use composition over function pointers
Browse files Browse the repository at this point in the history
Signed-off-by: Leonardo Di Giovanna <[email protected]>
  • Loading branch information
ekoops authored and poiana committed Feb 25, 2025
1 parent 756643d commit 1008768
Show file tree
Hide file tree
Showing 19 changed files with 257 additions and 98 deletions.
63 changes: 25 additions & 38 deletions pkg/test/step/syscall/base/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,10 @@ import (

"github.com/falcosecurity/event-generator/pkg/test/field"
"github.com/falcosecurity/event-generator/pkg/test/step"
"github.com/falcosecurity/event-generator/pkg/test/step/syscall"
)

// baseSyscall represents a generic system call test step.
type baseSyscall struct {
// Syscall provides a common implementation layer for system call test steps.
type Syscall struct {
// stepName is the syscall step name.
stepName string

Expand All @@ -47,30 +46,17 @@ type baseSyscall struct {

// fieldBindings is the list of field binding required to run the current system call step.
fieldBindings []*step.FieldBinding

// runFunc is the underlying implementation of the current step.
runFunc func(ctx context.Context) error
// cleanupFunc is the underlying implementation of the current step cleanup procedure.
cleanupFunc func(ctx context.Context) error
}

// Verify that baseSyscall implements syscall.Syscall interface.
var _ syscall.Syscall = (*baseSyscall)(nil)

var errOpenModeMustBePositive = fmt.Errorf("open mode must be a positive integer")

// New creates a new generic system call test step.
// New creates a new system call test step common implementation layer.
func New(stepName string, rawArgs map[string]any, fieldBindings []*step.FieldBinding, argsContainer,
bindOnlyArgsContainer, retValueContainer reflect.Value, defaultedArgs []string,
runFunc, cleanupFunc func(ctx context.Context) error) (syscall.Syscall, error) {
bindOnlyArgsContainer, retValueContainer reflect.Value, defaultedArgs []string) (*Syscall, error) {
if err := checkContainersInvariants(argsContainer, bindOnlyArgsContainer, retValueContainer); err != nil {
return nil, err
}

if runFunc == nil {
return nil, fmt.Errorf("run function must not be nil")
}

unboundArgs := field.Paths(argsContainer.Type())
boundArgs, err := setArgFieldValues(argsContainer, rawArgs)
if err != nil {
Expand All @@ -88,16 +74,14 @@ func New(stepName string, rawArgs map[string]any, fieldBindings []*step.FieldBin
delete(unboundBindOnlyArgs, arg)
}

s := &baseSyscall{
s := &Syscall{
stepName: stepName,
argsContainer: argsContainer,
bindOnlyArgsContainer: bindOnlyArgsContainer,
retValueContainer: retValueContainer,
unboundArgs: unboundArgs,
unboundBindOnlyArgs: unboundBindOnlyArgs,
fieldBindings: fieldBindings,
runFunc: runFunc,
cleanupFunc: cleanupFunc,
}
return s, nil
}
Expand Down Expand Up @@ -296,26 +280,28 @@ func setSubArgFieldValues(argField *field.Field, value any) ([]string, error) {
return boundArgs, nil
}

func (s *baseSyscall) Name() string {
// Name implements step.Step.Name method.
func (s *Syscall) Name() string {
return s.stepName
}

func (s *baseSyscall) Run(ctx context.Context) error {
if s.unboundArgFieldsNum() > 0 {
unboundArgFields := s.unboundArgFieldNames()
return fmt.Errorf("the following argument fields are not bound yet: %v", unboundArgFields)
// CheckUnboundArgField verifies that all argument fields are bound and returns an error in case of any unbound one.
func (s *Syscall) CheckUnboundArgField() error {
if s.unboundArgFieldsNum() == 0 {
return nil
}

return s.runFunc(ctx)
unboundArgFields := s.unboundArgFieldNames()
return fmt.Errorf("the following argument fields are not bound yet: %v", unboundArgFields)
}

// unboundArgFieldsNum returns the number of unbound argument fields.
func (s *baseSyscall) unboundArgFieldsNum() int {
func (s *Syscall) unboundArgFieldsNum() int {
return len(s.unboundArgs) + len(s.unboundBindOnlyArgs)
}

// unboundArgFieldNames returns the names of the unbound argument fields.
func (s *baseSyscall) unboundArgFieldNames() []string {
func (s *Syscall) unboundArgFieldNames() []string {
unboundArgFieldNames := make([]string, len(s.unboundArgs)+len(s.unboundBindOnlyArgs))
i := 0
for unboundArgFieldName := range s.unboundArgs {
Expand All @@ -329,29 +315,30 @@ func (s *baseSyscall) unboundArgFieldNames() []string {
return unboundArgFieldNames
}

func (s *baseSyscall) Cleanup(ctx context.Context) error {
if s.cleanupFunc != nil {
return s.cleanupFunc(ctx)
}

// Cleanup implements step.Step.Cleanup method.
func (s *Syscall) Cleanup(_ context.Context) error {
// No-op default implementation for syscall not needing a cleanup step.
return nil
}

func (s *baseSyscall) Field(name string) (*field.Field, error) {
// Field implements step.Step.Field method.
func (s *Syscall) Field(name string) (*field.Field, error) {
argFieldContainers := []reflect.Value{s.retValueContainer, s.argsContainer, s.bindOnlyArgsContainer}
return field.ByName(name, argFieldContainers...)
}

func (s *baseSyscall) Bind(bindings []*step.Binding) error {
// Bind implements step.Step.Bind method.
func (s *Syscall) Bind(bindings []*step.Binding) error {
argFieldContainers := []reflect.Value{s.argsContainer, s.bindOnlyArgsContainer}
return s.bindMultiple(bindings, argFieldContainers)
}

func (s *baseSyscall) FieldBindings() []*step.FieldBinding {
// FieldBindings implements step.Step.FieldBindings method.
func (s *Syscall) FieldBindings() []*step.FieldBinding {
return s.fieldBindings
}

func (s *baseSyscall) bindMultiple(bindings []*step.Binding, argFieldContainers []reflect.Value) error {
func (s *Syscall) bindMultiple(bindings []*step.Binding, argFieldContainers []reflect.Value) error {
for _, binding := range bindings {
if err := bindSingle(binding, argFieldContainers); err != nil {
return err
Expand Down
15 changes: 12 additions & 3 deletions pkg/test/step/syscall/connect/connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
)

type connectSyscall struct {
*base.Syscall
// args represents arguments that can be provided by value or by binding.
args struct {
FD int `field_type:"fd"`
Expand All @@ -43,11 +44,19 @@ func New(name string, rawArgs map[string]any, fieldBindings []*step.FieldBinding
argsContainer := reflect.ValueOf(&c.args).Elem()
bindOnlyArgsContainer := reflect.ValueOf(&c.bindOnlyArgs).Elem()
retValContainer := reflect.ValueOf(c).Elem()
return base.New(name, rawArgs, fieldBindings, argsContainer, bindOnlyArgsContainer, retValContainer, nil, c.run,
nil)
var err error
c.Syscall, err = base.New(name, rawArgs, fieldBindings, argsContainer, bindOnlyArgsContainer, retValContainer, nil)
if err != nil {
return nil, err
}
return c, nil
}

func (c *connectSyscall) run(_ context.Context) error {
func (c *connectSyscall) Run(_ context.Context) error {
if err := c.CheckUnboundArgField(); err != nil {
return err
}

if err := unix.Connect(c.args.FD, c.args.Address); err != nil {
return err
}
Expand Down
15 changes: 12 additions & 3 deletions pkg/test/step/syscall/dup/dup.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
)

type dupSyscall struct {
*base.Syscall
// args represents arguments that can be provided by value or by binding.
args struct {
OldFD int `field_type:"fd"`
Expand All @@ -42,11 +43,19 @@ func New(name string, rawArgs map[string]any, fieldBindings []*step.FieldBinding
argsContainer := reflect.ValueOf(&d.args).Elem()
bindOnlyArgsContainer := reflect.ValueOf(&d.bindOnlyArgs).Elem()
retValContainer := reflect.ValueOf(d).Elem()
return base.New(name, rawArgs, fieldBindings, argsContainer, bindOnlyArgsContainer, retValContainer, nil, d.run,
nil)
var err error
d.Syscall, err = base.New(name, rawArgs, fieldBindings, argsContainer, bindOnlyArgsContainer, retValContainer, nil)
if err != nil {
return nil, err
}
return d, nil
}

func (d *dupSyscall) run(_ context.Context) error {
func (d *dupSyscall) Run(_ context.Context) error {
if err := d.CheckUnboundArgField(); err != nil {
return err
}

fd, err := unix.Dup(d.args.OldFD)
if err != nil {
return err
Expand Down
17 changes: 13 additions & 4 deletions pkg/test/step/syscall/dup2/dup2.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
)

type dup2Syscall struct {
*base.Syscall
// args represents arguments that can be provided by value or by binding.
args struct {
OldFD int `field_type:"fd"`
Expand All @@ -47,11 +48,19 @@ func New(name string, rawArgs map[string]any, fieldBindings []*step.FieldBinding
argsContainer := reflect.ValueOf(&d.args).Elem()
bindOnlyArgsContainer := reflect.ValueOf(&d.bindOnlyArgs).Elem()
retValContainer := reflect.ValueOf(d).Elem()
return base.New(name, rawArgs, fieldBindings, argsContainer, bindOnlyArgsContainer, retValContainer, nil, d.run,
d.cleanup)
var err error
d.Syscall, err = base.New(name, rawArgs, fieldBindings, argsContainer, bindOnlyArgsContainer, retValContainer, nil)
if err != nil {
return nil, err
}
return d, nil
}

func (d *dup2Syscall) run(_ context.Context) error {
func (d *dup2Syscall) Run(_ context.Context) error {
if err := d.CheckUnboundArgField(); err != nil {
return err
}

if d.savedFD != -1 {
return fmt.Errorf("cannot re-run the step without performing cleanup first")
}
Expand All @@ -74,7 +83,7 @@ func (d *dup2Syscall) run(_ context.Context) error {
return nil
}

func (d *dup2Syscall) cleanup(_ context.Context) error {
func (d *dup2Syscall) Cleanup(_ context.Context) error {
if d.savedFD == -1 {
return nil
}
Expand Down
18 changes: 14 additions & 4 deletions pkg/test/step/syscall/dup3/dup3.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
)

type dup3Syscall struct {
*base.Syscall
// args represents arguments that can be provided by value or by binding.
args struct {
OldFD int `field_type:"fd"`
Expand All @@ -48,11 +49,20 @@ func New(name string, rawArgs map[string]any, fieldBindings []*step.FieldBinding
bindOnlyArgsContainer := reflect.ValueOf(&d.bindOnlyArgs).Elem()
retValContainer := reflect.ValueOf(d).Elem()
defaultedArgs := []string{"flags"}
return base.New(name, rawArgs, fieldBindings, argsContainer, bindOnlyArgsContainer, retValContainer, defaultedArgs,
d.run, d.cleanup)
var err error
d.Syscall, err = base.New(name, rawArgs, fieldBindings, argsContainer, bindOnlyArgsContainer, retValContainer,
defaultedArgs)
if err != nil {
return nil, err
}
return d, nil
}

func (d *dup3Syscall) run(_ context.Context) error {
func (d *dup3Syscall) Run(_ context.Context) error {
if err := d.CheckUnboundArgField(); err != nil {
return err
}

if d.savedFD != -1 {
return fmt.Errorf("cannot re-run the step without performing cleanup first")
}
Expand All @@ -75,7 +85,7 @@ func (d *dup3Syscall) run(_ context.Context) error {
return nil
}

func (d *dup3Syscall) cleanup(_ context.Context) error {
func (d *dup3Syscall) Cleanup(_ context.Context) error {
if d.savedFD == -1 {
return nil
}
Expand Down
16 changes: 13 additions & 3 deletions pkg/test/step/syscall/finitmodule/finitmodule.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
)

type finitModuleSyscall struct {
*base.Syscall
// args represents arguments that can be provided by value or by binding.
args struct {
ParamValues string `field_type:"module_params"`
Expand All @@ -48,10 +49,19 @@ func New(name string, rawArgs map[string]any, fieldBindings []*step.FieldBinding
bindOnlyArgsContainer := reflect.ValueOf(&f.bindOnlyArgs).Elem()
retValContainer := reflect.ValueOf(f).Elem()
defaultedArgs := []string{"paramvalues", "flags"}
return base.New(name, rawArgs, fieldBindings, argsContainer, bindOnlyArgsContainer, retValContainer, defaultedArgs,
f.run, nil)
var err error
f.Syscall, err = base.New(name, rawArgs, fieldBindings, argsContainer, bindOnlyArgsContainer, retValContainer,
defaultedArgs)
if err != nil {
return nil, err
}
return f, nil
}

func (f *finitModuleSyscall) run(_ context.Context) error {
func (f *finitModuleSyscall) Run(_ context.Context) error {
if err := f.CheckUnboundArgField(); err != nil {
return err
}

return unix.FinitModule(f.bindOnlyArgs.FD, f.args.ParamValues, f.args.Flags)
}
16 changes: 13 additions & 3 deletions pkg/test/step/syscall/initmodule/initmodule.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
)

type initModuleSyscall struct {
*base.Syscall
// args represents arguments that can be provided by value or by binding.
args struct {
ModuleImage []byte `field_type:"buffer"`
Expand All @@ -45,10 +46,19 @@ func New(name string, rawArgs map[string]any, fieldBindings []*step.FieldBinding
bindOnlyArgsContainer := reflect.ValueOf(&i.bindOnlyArgs).Elem()
retValContainer := reflect.ValueOf(i).Elem()
defaultedArgs := []string{"paramvalues"}
return base.New(name, rawArgs, fieldBindings, argsContainer, bindOnlyArgsContainer, retValContainer, defaultedArgs,
i.run, nil)
var err error
i.Syscall, err = base.New(name, rawArgs, fieldBindings, argsContainer, bindOnlyArgsContainer, retValContainer,
defaultedArgs)
if err != nil {
return nil, err
}
return i, nil
}

func (i *initModuleSyscall) run(_ context.Context) error {
func (i *initModuleSyscall) Run(_ context.Context) error {
if err := i.CheckUnboundArgField(); err != nil {
return err
}

return unix.InitModule(i.args.ModuleImage, i.args.ParamValues)
}
15 changes: 12 additions & 3 deletions pkg/test/step/syscall/kill/kill.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
)

type killSyscall struct {
*base.Syscall
// args represents arguments that can be provided by value or by binding.
args struct {
Sig unix.Signal `field_type:"signal"`
Expand All @@ -44,10 +45,18 @@ func New(name string, rawArgs map[string]any, fieldBindings []*step.FieldBinding
argsContainer := reflect.ValueOf(&k.args).Elem()
bindOnlyArgsContainer := reflect.ValueOf(&k.bindOnlyArgs).Elem()
retValContainer := reflect.ValueOf(k).Elem()
return base.New(name, rawArgs, fieldBindings, argsContainer, bindOnlyArgsContainer, retValContainer, nil, k.run,
nil)
var err error
k.Syscall, err = base.New(name, rawArgs, fieldBindings, argsContainer, bindOnlyArgsContainer, retValContainer, nil)
if err != nil {
return nil, err
}
return k, nil
}

func (k *killSyscall) run(_ context.Context) error {
func (k *killSyscall) Run(_ context.Context) error {
if err := k.CheckUnboundArgField(); err != nil {
return err
}

return unix.Kill(k.bindOnlyArgs.PID, k.args.Sig)
}
Loading

0 comments on commit 1008768

Please sign in to comment.