gorules / zen

Open-source Business Rules Engine for your Rust, NodeJS, Python or Go applications.
https://gorules.io
MIT License
768 stars 74 forks source link

ZenEngine Crashes when very big number passed in context parameter #175

Open jazarja opened 4 months ago

jazarja commented 4 months ago

Description: When passing a large integer (bigint) as a value in JSON data into ZenEngine version 0.23.0, the application crashes with the following error:

thread 'tokio-runtime-worker' panicked at core\expression\src\isolate.rs:62:55: called Result::unwrap() on an Err value: () stack backtrace: 0: 0x7ffd15de48da - napi_register_module_v1 1: 0x7ffd15cfe60b - 2: 0x7ffd15ddbdc1 - napi_register_module_v1 3: 0x7ffd15de6296 - napi_register_module_v1 4: 0x7ffd15de5c1f - napi_register_module_v1 5: 0x7ffd15de6a2a - napi_register_module_v1 6: 0x7ffd15de6509 - napi_register_module_v1 7: 0x7ffd15de6449 - napi_register_module_v1 8: 0x7ffd15de6436 - napi_register_module_v1 9: 0x7ffd15f04c37 - napi_register_module_v1 10: 0x7ffd15f05093 - napi_register_module_v1 11: 0x7ffd15e1c7cd - napi_register_module_v1 12: 0x7ffd15c0a4e8 - 13: 0x7ffd15c1bca0 - 14: 0x7ffd15ca2b27 - 15: 0x7ffd15c5a8a2 - 16: 0x7ffd15dedf67 - napi_register_module_v1 17: 0x7ffd15dfb349 - napi_register_module_v1 18: 0x7ffd15df35be - napi_register_module_v1 19: 0x7ffd15df2ff1 - napi_register_module_v1 20: 0x7ffd15de458c - napi_register_module_v1 22: 0x7ffdac8026b1 - RtlUserThreadStart

The stack trace is provided in the issue report.

This issue was reproduced using the following sample JSON data:

{
  "enterprise_value": 150945677535289400
}

Code

export async function executeGrader(graderJsonPath: string, data: object, params: object): Promise<any> {
    const content = await fs.readFileSync(graderJsonPath);
    const engine = new ZenEngine();

    const decision = engine.createDecision(content);
    const result = await decision.evaluate({ ...params, ...data });
    if (result?.result?.grade) {
        return result.result.grade;
    }

    engine.dispose();
    return null;
}

Environment:

Node.js version: 18.17.1 ZenEngine version: 0.23.0

ivanmiletic commented 3 months ago

Issue seems to be connected to NAPI - a way to build native addons for node. We will take a look

braquino commented 3 months ago

I have the same issue using Go. It cannot handle float32. Testes with: 9e34 and 9e-35

rule-engine % make test
go test ./internals/db/ ./internals/engine/
ok      gitlab.com/a2asc/smp/rule-engine/internals/db   (cached)
PWD: /Users/b.goncalvesdeaquino/repos/smartplatform/components/rule-engine/internals/engine
problem caused by: 9e+34
thread '<unnamed>' panicked at core/expression/src/isolate.rs:62:55:
called `Result::unwrap()` on an `Err` value: ()
stack backtrace:
   0:        0x100c5600c - _rust_eh_personality
   1:        0x100ba47cc - _zen_engine_new_golang
   2:        0x100c56d2c - _rust_eh_personality
   3:        0x100c58030 - _rust_eh_personality
   4:        0x100c56f44 - _rust_eh_personality
   5:        0x100c56ea8 - _rust_eh_personality
   6:        0x100c56e9c - _rust_eh_personality
   7:        0x100d44278 - _bf_get_fft_size
   8:        0x100d44524 - _bf_get_fft_size
   9:        0x100c6c9c4 - _rust_eh_personality
  10:        0x100b59378 - _crosscall1
  11:        0x100b633b8 - _crosscall1
  12:        0x100b7c078 - _zen_decision_evaluate
  13:        0x100b49744 - __cgo_e7b9b5fdf589_Cfunc_zen_decision_evaluate
fatal runtime error: failed to initiate panic, error 5
SIGABRT: abort
PC=0x1827220dc m=0 sigcode=0
signal arrived during cgo execution

goroutine 36 gp=0x140001048c0 m=0 mp=0x100feb700 [syscall]:
runtime.cgocall(0x100b49718, 0x1400016fc88)
        /usr/local/go/src/runtime/cgocall.go:157 +0x44 fp=0x1400016fc50 sp=0x1400016fc10 pc=0x1009e6cc4
github.com/gorules/zen-go._Cfunc_zen_decision_evaluate(0x122f2bf50, 0x113009000, {0x0, 0x1})
        _cgo_gotypes.go:164 +0x3c fp=0x1400016fc80 sp=0x1400016fc50 pc=0x100b46aec
github.com/gorules/zen-go.decision.EvaluateWithOpts({0x1400011f350?}, {0x100e948e0?, 0x1400011f350?}, {0xc0?, 0x21?})
        /Users/b.goncalvesdeaquino/go/pkg/mod/github.com/gorules/zen-go@v0.3.0/decision.go:41 +0xc4 fp=0x1400016fd90 sp=0x1400016fc80 pc=0x100b46de4
github.com/gorules/zen-go.decision.Evaluate({0x1400015c180?}, {0x100e948e0?, 0x1400011f350?})
        /Users/b.goncalvesdeaquino/go/pkg/mod/github.com/gorules/zen-go@v0.3.0/decision.go:24 +0x2c fp=0x1400016fdc0 sp=0x1400016fd90 pc=0x100b46cec
gitlab.com/a2asc/smp/rule-engine/internals/engine.(*ZenRule).Apply(0x14000158320, 0x1400015c180)
        /Users/b.goncalvesdeaquino/repos/smartplatform/components/rule-engine/internals/engine/zenrule.go:92 +0xa8 fp=0x1400016fe40 sp=0x1400016fdc0 pc=0x100b48568
gitlab.com/a2asc/smp/rule-engine/internals/engine.TestRulesBigNumbers(0x14000134d00)
        /Users/b.goncalvesdeaquino/repos/smartplatform/components/rule-engine/internals/engine/zenrule_test.go:90 +0x17c fp=0x1400016ff60 sp=0x1400016fe40 pc=0x100b48f3c
testing.tRunner(0x14000134d00, 0x100ec2900)
        /usr/local/go/src/testing/testing.go:1689 +0xec fp=0x1400016ffb0 sp=0x1400016ff60 pc=0x100ab93bc
testing.(*T).Run.gowrap1()
        /usr/local/go/src/testing/testing.go:1742 +0x2c fp=0x1400016ffd0 sp=0x1400016ffb0 pc=0x100aba20c
runtime.goexit({})
        /usr/local/go/src/runtime/asm_arm64.s:1222 +0x4 fp=0x1400016ffd0 sp=0x1400016ffd0 pc=0x100a54b34
created by testing.(*T).Run in goroutine 1
        /usr/local/go/src/testing/testing.go:1742 +0x318

goroutine 1 gp=0x140000021c0 m=nil [chan receive]:
runtime.gopark(0x1400016d9c8?, 0x140001256c0?, 0x48?, 0x57?, 0x140001256c0?)
        /usr/local/go/src/runtime/proc.go:402 +0xc8 fp=0x1400016d990 sp=0x1400016d970 pc=0x100a1dfb8
runtime.chanrecv(0x1400017a460, 0x1400016da8f, 0x1)
        /usr/local/go/src/runtime/chan.go:583 +0x404 fp=0x1400016da10 sp=0x1400016d990 pc=0x1009e9214
runtime.chanrecv1(0x100fea500?, 0x100e8ae00?)
        /usr/local/go/src/runtime/chan.go:442 +0x14 fp=0x1400016da40 sp=0x1400016da10 pc=0x1009e8e04
testing.(*T).Run(0x14000134680, {0x100d5429c?, 0x1400016db38?}, 0x100ec2900)
        /usr/local/go/src/testing/testing.go:1750 +0x32c fp=0x1400016db00 sp=0x1400016da40 pc=0x100aba0dc
testing.runTests.func1(0x14000134680)
        /usr/local/go/src/testing/testing.go:2161 +0x40 fp=0x1400016db40 sp=0x1400016db00 pc=0x100abbeb0
testing.tRunner(0x14000134680, 0x1400016dc58)
        /usr/local/go/src/testing/testing.go:1689 +0xec fp=0x1400016db90 sp=0x1400016db40 pc=0x100ab93bc
testing.runTests(0x1400012a150, {0x100fb30e0, 0x2, 0x2}, {0x1400016dd18?, 0x100aa6ac0?, 0x100fea940?})
        /usr/local/go/src/testing/testing.go:2159 +0x3b0 fp=0x1400016dc80 sp=0x1400016db90 pc=0x100abbdb0
testing.(*M).Run(0x140001265a0)
        /usr/local/go/src/testing/testing.go:2027 +0x5a4 fp=0x1400016deb0 sp=0x1400016dc80 pc=0x100abaae4
main.main()
        _testmain.go:49 +0x16c fp=0x1400016df40 sp=0x1400016deb0 pc=0x100b495dc
runtime.main()
        /usr/local/go/src/runtime/proc.go:271 +0x28c fp=0x1400016dfd0 sp=0x1400016df40 pc=0x100a1db5c
runtime.goexit({})
        /usr/local/go/src/runtime/asm_arm64.s:1222 +0x4 fp=0x1400016dfd0 sp=0x1400016dfd0 pc=0x100a54b34

goroutine 18 gp=0x14000094380 m=nil [force gc (idle)]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
        /usr/local/go/src/runtime/proc.go:402 +0xc8 fp=0x14000056790 sp=0x14000056770 pc=0x100a1dfb8
runtime.goparkunlock(...)
        /usr/local/go/src/runtime/proc.go:408
runtime.forcegchelper()
        /usr/local/go/src/runtime/proc.go:326 +0xb8 fp=0x140000567d0 sp=0x14000056790 pc=0x100a1de18
runtime.goexit({})
        /usr/local/go/src/runtime/asm_arm64.s:1222 +0x4 fp=0x140000567d0 sp=0x140000567d0 pc=0x100a54b34
created by runtime.init.6 in goroutine 1
        /usr/local/go/src/runtime/proc.go:314 +0x24

goroutine 19 gp=0x14000094540 m=nil [GC sweep wait]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
        /usr/local/go/src/runtime/proc.go:402 +0xc8 fp=0x14000056f60 sp=0x14000056f40 pc=0x100a1dfb8
runtime.goparkunlock(...)
        /usr/local/go/src/runtime/proc.go:408
runtime.bgsweep(0x140000a2000)
        /usr/local/go/src/runtime/mgcsweep.go:278 +0xa0 fp=0x14000056fb0 sp=0x14000056f60 pc=0x100a08910
runtime.gcenable.gowrap1()
        /usr/local/go/src/runtime/mgc.go:203 +0x28 fp=0x14000056fd0 sp=0x14000056fb0 pc=0x1009fcb18
runtime.goexit({})
        /usr/local/go/src/runtime/asm_arm64.s:1222 +0x4 fp=0x14000056fd0 sp=0x14000056fd0 pc=0x100a54b34
created by runtime.gcenable in goroutine 1
        /usr/local/go/src/runtime/mgc.go:203 +0x6c

goroutine 20 gp=0x14000094700 m=nil [GC scavenge wait]:
runtime.gopark(0x140000a2000?, 0x100da6ac0?, 0x1?, 0x0?, 0x14000094700?)
        /usr/local/go/src/runtime/proc.go:402 +0xc8 fp=0x14000057760 sp=0x14000057740 pc=0x100a1dfb8
runtime.goparkunlock(...)
        /usr/local/go/src/runtime/proc.go:408
runtime.(*scavengerState).park(0x100fead00)
        /usr/local/go/src/runtime/mgcscavenge.go:425 +0x5c fp=0x14000057790 sp=0x14000057760 pc=0x100a062fc
runtime.bgscavenge(0x140000a2000)
        /usr/local/go/src/runtime/mgcscavenge.go:653 +0x44 fp=0x140000577b0 sp=0x14000057790 pc=0x100a06854
runtime.gcenable.gowrap2()
        /usr/local/go/src/runtime/mgc.go:204 +0x28 fp=0x140000577d0 sp=0x140000577b0 pc=0x1009fcab8
runtime.goexit({})
        /usr/local/go/src/runtime/asm_arm64.s:1222 +0x4 fp=0x140000577d0 sp=0x140000577d0 pc=0x100a54b34
created by runtime.gcenable in goroutine 1
        /usr/local/go/src/runtime/mgc.go:204 +0xac

goroutine 34 gp=0x14000104700 m=nil [finalizer wait]:
runtime.gopark(0x0?, 0x0?, 0xc8?, 0xa5?, 0x100b145b0?)
        /usr/local/go/src/runtime/proc.go:402 +0xc8 fp=0x1400005a580 sp=0x1400005a560 pc=0x100a1dfb8
runtime.runfinq()
        /usr/local/go/src/runtime/mfinal.go:194 +0x108 fp=0x1400005a7d0 sp=0x1400005a580 pc=0x1009fbbe8
runtime.goexit({})
        /usr/local/go/src/runtime/asm_arm64.s:1222 +0x4 fp=0x1400005a7d0 sp=0x1400005a7d0 pc=0x100a54b34
created by runtime.createfing in goroutine 1
        /usr/local/go/src/runtime/mfinal.go:164 +0x80

r0      0x0
r1      0x0
r2      0x0
r3      0x0
r4      0x0
r5      0x1
r6      0x6261745f74706563
r7      0x630
r8      0x4a609877eb82703a
r9      0x4a6098763280ac7a
r10     0x0
r11     0x110000
r12     0x100e25fe0
r13     0x0
r14     0x0
r15     0x100e20d88
r16     0x148
r17     0x1e20b32f8
r18     0x0
r19     0x6
r20     0x1d902dc40
r21     0x103
r22     0x1d902dd20
r23     0x122f04340
r24     0x100e70228
r25     0x16f41e5e0
r26     0x100e020e8
r27     0x1
r28     0x100e70290
r29     0x16f41d410
lr      0x182759cc0
sp      0x16f41d3f0
pc      0x1827220dc
fault   0x1827220dc
FAIL    gitlab.com/a2asc/smp/rule-engine/internals/engine       0.286s
FAIL
make: *** [test] Error 1
stefan-gorules commented 3 months ago

Hi @braquino,

Unfortunately we aren't able to handle numbers with arbitrary precision at the moment. Out of interest, what is your use case for dealing with numbers of e35/e-35. Reason why we've went with decimal instead of float is to keep precision which is important when dealing with financial calculations.

braquino commented 2 months ago

@stefan-gorules my thought was: if it is an invalid number, decision.Evaluate should return an error, but in this case it crashes without chance to handle the error. This behavior makes it complicated to use, I had to put a check on every float32 on my structs to avoid send a "bad number" (that is json valid) to gorules. What do you think ?

stefan-gorules commented 2 months ago

@braquino Yep, agree. This is a sound way of handling the scenario. I'll have a look into this.