Skip to content

Commit f5fcf4f

Browse files
committed
[CRE-954] Extra logging limits in WASM Module
+ set a missing "mode" field on executor
1 parent 415d51d commit f5fcf4f

File tree

4 files changed

+102
-0
lines changed

4 files changed

+102
-0
lines changed

pkg/workflows/wasm/host/execution.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ type execution[T any] struct {
2727
mode sdkpb.Mode
2828
donSeed int64
2929
nodeSeed int64
30+
donLogCount uint32
31+
nodeLogCount uint32
3032
}
3133

3234
// callCapAsync async calls a capability by placing execution results onto a
@@ -139,6 +141,32 @@ func (e *execution[T]) awaitSecrets(ctx context.Context, acr *sdkpb.AwaitSecrets
139141
}
140142

141143
func (e *execution[T]) log(caller *wasmtime.Caller, ptr int32, ptrlen int32) {
144+
switch e.mode {
145+
case sdkpb.Mode_MODE_DON:
146+
e.donLogCount++
147+
if e.donLogCount == e.module.cfg.MaxLogCountDONMode {
148+
e.module.cfg.Logger.Errorf("max log count for don mode reached: %d - all subsequent logs will be dropped", e.donLogCount)
149+
}
150+
if e.donLogCount > e.module.cfg.MaxLogCountDONMode {
151+
// silently drop to avoid spamming logs
152+
return
153+
}
154+
case sdkpb.Mode_MODE_NODE:
155+
e.nodeLogCount++
156+
if e.nodeLogCount == e.module.cfg.MaxLogCountNodeMode {
157+
e.module.cfg.Logger.Errorf("max log count for node mode reached: %d - all subsequent logs will be dropped", e.nodeLogCount)
158+
}
159+
if e.nodeLogCount > e.module.cfg.MaxLogCountNodeMode {
160+
// silently drop to avoid spamming logs
161+
return
162+
}
163+
}
164+
165+
if ptrlen > int32(e.module.cfg.MaxLogLenBytes) {
166+
e.module.cfg.Logger.Errorf("log message too long: %d - dropping", ptrlen)
167+
return
168+
}
169+
142170
b, innerErr := wasmRead(caller, ptr, ptrlen)
143171
if innerErr != nil {
144172
e.module.cfg.Logger.Errorf("error calling log: %s", innerErr)

pkg/workflows/wasm/host/module.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ var (
4141
defaultMaxCompressedBinarySize = 20 * 1024 * 1024 // 20 MB
4242
defaultMaxDecompressedBinarySize = 100 * 1024 * 1024 // 100 MB
4343
defaultMaxResponseSizeBytes = 5 * 1024 * 1024 // 5 MB
44+
defaultMaxLogLenBytes = 1024 * 1024 // 1 MB
45+
defaultMaxLogCountDONMode = 10_000
46+
defaultMaxLogCountNodeMode = 10_000
4447
ResponseBufferTooSmall = "response buffer too small"
4548
)
4649

@@ -62,6 +65,10 @@ type ModuleConfig struct {
6265
MaxDecompressedBinarySize uint64
6366
MaxResponseSizeBytes uint64
6467

68+
MaxLogLenBytes uint32
69+
MaxLogCountDONMode uint32
70+
MaxLogCountNodeMode uint32
71+
6572
// Labeler is used to emit messages from the module.
6673
Labeler custmsg.MessageEmitter
6774

@@ -183,6 +190,15 @@ func NewModule(modCfg *ModuleConfig, binary []byte, opts ...func(*ModuleConfig))
183190
if modCfg.MaxResponseSizeBytes == 0 {
184191
modCfg.MaxResponseSizeBytes = uint64(defaultMaxResponseSizeBytes)
185192
}
193+
if modCfg.MaxLogLenBytes == 0 {
194+
modCfg.MaxLogLenBytes = uint32(defaultMaxLogLenBytes)
195+
}
196+
if modCfg.MaxLogCountDONMode == 0 {
197+
modCfg.MaxLogCountDONMode = uint32(defaultMaxLogCountDONMode)
198+
}
199+
if modCfg.MaxLogCountNodeMode == 0 {
200+
modCfg.MaxLogCountNodeMode = uint32(defaultMaxLogCountNodeMode)
201+
}
186202

187203
// Take the max of the min and the configured max memory mbs.
188204
// We do this because Go requires a minimum of 16 megabytes to run,
@@ -529,6 +545,7 @@ func runWasm[I, O proto.Message](
529545
secretsResponses: map[int32]<-chan *secretsResponse{},
530546
module: m,
531547
executor: helper,
548+
mode: sdkpb.Mode_MODE_DON,
532549
donSeed: donSeed,
533550
nodeSeed: int64(rand.Uint64()),
534551
}

pkg/workflows/wasm/host/standard_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/smartcontractkit/chainlink-common/pkg/capabilities/v2/protoc/pkg/test_capabilities/basicaction"
3030
"github.com/smartcontractkit/chainlink-common/pkg/capabilities/v2/protoc/pkg/test_capabilities/basictrigger"
3131
"github.com/smartcontractkit/chainlink-common/pkg/capabilities/v2/protoc/pkg/test_capabilities/nodeaction"
32+
"github.com/smartcontractkit/chainlink-common/pkg/logger"
3233
"github.com/smartcontractkit/chainlink-protos/cre/go/sdk"
3334
"github.com/smartcontractkit/chainlink-protos/cre/go/values"
3435
valuespb "github.com/smartcontractkit/chainlink-protos/cre/go/values/pb"
@@ -234,6 +235,44 @@ func TestStandardLogging(t *testing.T) {
234235
runWithBasicTrigger(t, mockExecutionHelper)
235236
}
236237

238+
func TestStandardLoggingWithLimits(t *testing.T) {
239+
t.Parallel()
240+
mockExecutionHelper := NewMockExecutionHelper(t)
241+
mockExecutionHelper.EXPECT().GetWorkflowExecutionID().Return("id")
242+
mockExecutionHelper.EXPECT().GetNodeTime().RunAndReturn(func() time.Time {
243+
return time.Now()
244+
}).Maybe()
245+
mockExecutionHelper.EXPECT().GetDONTime().RunAndReturn(func() (time.Time, error) {
246+
return time.Now(), nil
247+
}).Maybe()
248+
249+
logs := []string{}
250+
mockExecutionHelper.EXPECT().EmitUserLog(mock.Anything).RunAndReturn(func(s string) error {
251+
logs = append(logs, s)
252+
return nil
253+
})
254+
255+
trigger := &basictrigger.Outputs{CoolOutput: anyTestTriggerValue}
256+
executeRequest := triggerExecuteRequest(t, 0, trigger)
257+
cfg := &ModuleConfig{
258+
Logger: logger.Test(t),
259+
IsUncompressed: true,
260+
MaxLogLenBytes: 20,
261+
MaxLogCountDONMode: 3,
262+
MaxLogCountNodeMode: 3,
263+
}
264+
265+
m := makeTestModuleByName(t, "logging_limits", cfg)
266+
_, err := m.Execute(t.Context(), executeRequest, mockExecutionHelper)
267+
require.NoError(t, err)
268+
269+
// allowed 3 logs max, one of which got rejected because it was too long
270+
// so expect 2 logs to be emitted
271+
require.Equal(t, 2, len(logs))
272+
require.Equal(t, "short log 1", logs[0])
273+
require.Equal(t, "short log 3", logs[1])
274+
}
275+
237276
func TestStandardMultipleTriggers(t *testing.T) {
238277
t.Parallel()
239278
m := makeTestModule(t)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package main
2+
3+
import (
4+
"github.com/smartcontractkit/chainlink-common/pkg/workflows/wasm/host/internal/rawsdk"
5+
)
6+
7+
func main() {
8+
request := rawsdk.GetRequest()
9+
msg := []byte("short log 1")
10+
rawsdk.Log(rawsdk.BufferToPointerLen(msg))
11+
msg = []byte("super duper excessively long log 2") // exceeding 20 byte limit set in the test
12+
rawsdk.Log(rawsdk.BufferToPointerLen(msg))
13+
msg = []byte("short log 3")
14+
rawsdk.Log(rawsdk.BufferToPointerLen(msg))
15+
msg = []byte("short log 4")
16+
rawsdk.Log(rawsdk.BufferToPointerLen(msg))
17+
rawsdk.SendResponse(request.Config)
18+
}

0 commit comments

Comments
 (0)