Skip to content

Commit

Permalink
Merge pull request inspektor-gadget#3449 from inspektor-gadget/mauric…
Browse files Browse the repository at this point in the history
…io/wasm-parameters

 wasm: Add parameters handling to wasm
  • Loading branch information
mauriciovasquezbernal authored Sep 19, 2024
2 parents 938b241 + 0f85578 commit df3462a
Show file tree
Hide file tree
Showing 17 changed files with 312 additions and 19 deletions.
30 changes: 30 additions & 0 deletions docs/gadget-devel/gadget-wasm-api-raw.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,3 +280,33 @@ Parameters:

Return value:
- None

### Parameters

Parameters passed to the WASM module are defined in the metadata file as this:

```yaml
...
params:
wasm:
param-key:
key: param-key
description: param-description
defaultValue: param-default-value
typeHint: param-type-hint
title: param-title
alias: param-alias
isMandatory: true
param-key2:
...
```
#### `getParamValue(key string) string`

Return the value of a parameter.

Parameters:
- `key` (string): Key of the parameter.

Return value:
- The value of the parameter.
14 changes: 3 additions & 11 deletions pkg/operators/wasm/fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,21 +72,13 @@ func (i *wasmOperatorInstance) fieldGet(ctx context.Context, m wapi.Module, stac
}

handleBytes := func(buf []byte) uint64 {
res, err := i.guestMalloc.Call(ctx, uint64(len(buf)))
val, err := i.writeToGuestMemory(ctx, buf)
if err != nil {
i.logger.Warnf("malloc failed: %v", err)
stack[0] = 0
return 0

}

if !m.Memory().Write(uint32(res[0]), buf) {
i.logger.Warnf("out of memory write")
stack[0] = 0
i.logger.Warnf("fieldGet: writing bytes to guest memory: %v", err)
return 0
}

return uint64(len(buf))<<32 | uint64(res[0])
return val
}

var val uint64
Expand Down
19 changes: 19 additions & 0 deletions pkg/operators/wasm/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package wasm
import (
"context"
"errors"
"fmt"

"github.com/tetratelabs/wazero"
wapi "github.com/tetratelabs/wazero/api"
Expand All @@ -33,6 +34,11 @@ func bufFromStack(m wapi.Module, val uint64) ([]byte, error) {
}

func stringFromStack(m wapi.Module, val uint64) (string, error) {
// handle empty strings in a special way
if val == 0 {
return "", nil
}

buf, err := bufFromStack(m, val)
if err != nil {
return "", err
Expand All @@ -50,3 +56,16 @@ func exportFunction(
WithGoModuleFunction(wapi.GoModuleFunc(fn), params, results).
Export(name)
}

func (i *wasmOperatorInstance) writeToGuestMemory(ctx context.Context, buf []byte) (uint64, error) {
res, err := i.guestMalloc.Call(ctx, uint64(len(buf)))
if err != nil {
return 0, fmt.Errorf("malloc failed: %w", err)
}

if !i.mod.Memory().Write(uint32(res[0]), buf) {
return 0, fmt.Errorf("out of memory write")
}

return uint64(len(buf))<<32 | uint64(res[0]), nil
}
64 changes: 64 additions & 0 deletions pkg/operators/wasm/params.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright 2024 The Inspektor Gadget authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package wasm

import (
"context"

"github.com/tetratelabs/wazero"
wapi "github.com/tetratelabs/wazero/api"
)

func (i *wasmOperatorInstance) addParamsFuncs(env wazero.HostModuleBuilder) {
exportFunction(env, "getParamValue", i.getParamValue,
[]wapi.ValueType{
wapi.ValueTypeI64, // ParamKey
},
[]wapi.ValueType{wapi.ValueTypeI64}, // Value
)
}

// getParamValue returns the value of a param.
// Params:
// - stack[0] parameter key
// Return value:
// - Uint64 with the param's value, 0 on error
func (i *wasmOperatorInstance) getParamValue(ctx context.Context, m wapi.Module, stack []uint64) {
paramKeyPtr := stack[0]

paramKey, err := stringFromStack(m, paramKeyPtr)
if err != nil {
i.logger.Warnf("getParamValue: reading string from stack: %v", err)
stack[0] = 0
return
}

val, ok := i.paramValues[paramKey]
if !ok {
i.logger.Warnf("getParamValue: param %q not found", paramKey)
stack[0] = 0
return
}

buf := []byte(val)
ret, err := i.writeToGuestMemory(ctx, buf)
if err != nil {
i.logger.Warnf("getParamValue: writing to guest memory: %v", err)
stack[0] = 0
return
}

stack[0] = ret
}
1 change: 1 addition & 0 deletions pkg/operators/wasm/testdata/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ TEST_ARTIFACTS = \
fields \
dataarray \
badguest \
params \
#

all: $(TEST_ARTIFACTS)
Expand Down
Binary file modified pkg/operators/wasm/testdata/badguest.tar
Binary file not shown.
2 changes: 1 addition & 1 deletion pkg/operators/wasm/testdata/badguest/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module main

go 1.22.0
go 1.22.7

require github.com/inspektor-gadget/inspektor-gadget v0.27.0

Expand Down
13 changes: 10 additions & 3 deletions pkg/operators/wasm/testdata/badguest/program.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ func fieldGet(acc uint32, data uint32, kind uint32) uint64
//go:wasmimport env fieldSet
func fieldSet(acc uint32, data uint32, kind uint32, value uint64) uint32

//go:wasmimport env getParamValue
func getParamValue(key uint64) uint64

func stringToBufPtr(s string) uint64 {
unsafePtr := unsafe.Pointer(unsafe.StringData(s))
return uint64(len(s))<<32 | uint64(uintptr(unsafePtr))
Expand All @@ -99,19 +102,19 @@ func logAndPanic(msg string) {
panic(msg)
}

func assertZero(v uint32, msg string) {
func assertZero[T uint64 | uint32](v T, msg string) {
if v != 0 {
logAndPanic(fmt.Sprintf("%d is not zero: %s", v, msg))
}
}

func assertNonZero(v uint32, msg string) {
func assertNonZero[T uint64 | uint32](v T, msg string) {
if v == 0 {
logAndPanic(fmt.Sprintf("v is zero: %s", msg))
}
}

func assertEqual(v1, v2 uint32, msg string) {
func assertEqual[T uint64 | uint32](v1, v2 T, msg string) {
if v1 != v2 {
logAndPanic(fmt.Sprintf("%d != %d: %s", v1, v2, msg))
}
Expand Down Expand Up @@ -240,6 +243,10 @@ func gadgetInit() int {
fieldGet(fieldHandle, fieldHandle, uint32(api.Kind_Uint32))
fieldGet(dataHandle, dataHandle, uint32(api.Kind_Uint32))

/* Params */
assertZero(getParamValue(stringToBufPtr("non-existing-param")), "getParamValue: not-found")
assertZero(getParamValue(invalidStrPtr), "getParamValue: invalid key ptr")

return 0
}

Expand Down
Binary file added pkg/operators/wasm/testdata/params.tar
Binary file not shown.
1 change: 1 addition & 0 deletions pkg/operators/wasm/testdata/params/build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
wasm: program.go
11 changes: 11 additions & 0 deletions pkg/operators/wasm/testdata/params/gadget.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
name: params_test
params:
wasm:
param-key:
key: param-key
description: param-description
defaultValue: param-default-value
typeHint: param-type-hint
title: param-title
alias: param-alias
isMandatory: true
8 changes: 8 additions & 0 deletions pkg/operators/wasm/testdata/params/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module main

go 1.22.7

require github.com/inspektor-gadget/inspektor-gadget v0.27.0

// use this to be able to compile it locally
replace github.com/inspektor-gadget/inspektor-gadget => ../../../../../
1 change: 1 addition & 0 deletions pkg/operators/wasm/testdata/params/program.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// TODO: a c file is always needed by the gadget to be built
48 changes: 48 additions & 0 deletions pkg/operators/wasm/testdata/params/program.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2024 The Inspektor Gadget authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// This program tries as hard as it can to break the host by calling functions
// with wrong arguments. It uses the low level functions directly as the goal is
// to test the host and not the wrapper API. Tests under dataarray and fields
// test also the higher level API.
package main

import (
api "github.com/inspektor-gadget/inspektor-gadget/wasmapi/go"
)

//export gadgetStart
func gadgetStart() int {
val, err := api.GetParamValue("param-key")
if err != nil {
api.Errorf("failed to get param: %v", err)
return 1
}

const expected = "param-value"
if val != expected {
api.Errorf("param value should be %q, got: %q", expected, val)
return 1
}

_, err = api.GetParamValue("non-existing-param")
if err == nil {
api.Errorf("looking for non-existing-param succeded")
return 1
}

return 0
}

func main() {}
31 changes: 27 additions & 4 deletions pkg/operators/wasm/wasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

"github.com/hashicorp/go-multierror"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/spf13/viper"
"github.com/tetratelabs/wazero"
wapi "github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
Expand Down Expand Up @@ -65,16 +66,34 @@ func (w *wasmOperator) InstantiateImageOperator(
operators.ImageOperatorInstance, error,
) {
instance := &wasmOperatorInstance{
gadgetCtx: gadgetCtx,
handleMap: map[uint32]any{},
logger: gadgetCtx.Logger(),
gadgetCtx: gadgetCtx,
handleMap: map[uint32]any{},
logger: gadgetCtx.Logger(),
paramValues: paramValues,
}

if err := instance.init(gadgetCtx, target, desc); err != nil {
instance.close(gadgetCtx)
return nil, fmt.Errorf("initializing wasm: %w", err)
}

var config *viper.Viper
if configVar, ok := gadgetCtx.GetVar("config"); ok {
config, _ = configVar.(*viper.Viper)
}

if config != nil {
extraParams := map[string]*api.Param{}
err := config.UnmarshalKey("params.wasm", &extraParams)
if err != nil {
return nil, fmt.Errorf("unmarshalling extra params: %w", err)
}

for _, v := range extraParams {
instance.extraParams = append(instance.extraParams, v)
}
}

return instance, nil
}

Expand All @@ -94,6 +113,9 @@ type wasmOperatorInstance struct {
handleMap map[uint32]any
lastHandleIndex uint32
handleLock sync.RWMutex

extraParams api.Params
paramValues map[string]string
}

func (i *wasmOperatorInstance) Name() string {
Expand All @@ -109,7 +131,7 @@ func (i *wasmOperatorInstance) Prepare(gadgetCtx operators.GadgetContext) error
}

func (i *wasmOperatorInstance) ExtraParams(gadgetCtx operators.GadgetContext) api.Params {
return nil
return i.extraParams
}

func (i *wasmOperatorInstance) addHandle(obj any) uint32 {
Expand Down Expand Up @@ -190,6 +212,7 @@ func (i *wasmOperatorInstance) init(
i.addLogFuncs(env)
i.addDataSourceFuncs(env)
i.addFieldFuncs(env)
i.addParamsFuncs(env)

if _, err := env.Instantiate(ctx); err != nil {
return fmt.Errorf("instantiating host module: %w", err)
Expand Down
Loading

0 comments on commit df3462a

Please sign in to comment.