Open pynixwang opened 5 years ago
Good find. For me this is the output I get:
icr(0.28.0) > BigDecimal.new("1e10000000000")
GC Warning: Failed to expand heap by 4179820544 bytes
GC Warning: Failed to expand heap by 4179689472 bytes
GC Warning: Out of Memory! Heap size: 0 MiB. Returning NULL!
Invalid memory access (signal 11) at address 0x0
[0x55fa4df27dc6] *CallStack::print_backtrace:Int32 +118
[0x55fa4df1b300] __crystal_sigfault_handler +192
[0x7f5b6ee944d0] ???
[0x7f5b6efa42d0] __gmpz_n_pow_ui +352
[0x55fa4df69872] *BigInt#**<UInt64>:BigInt +146
[0x55fa4df6b361] *BigDecimal#initialize<String>:UInt64 +4673
[0x55fa4df6a0da] *BigDecimal::new<String>:BigDecimal +90
[0x55fa4df1b3c2] *__icr_exec__:BigDecimal +34
[0x55fa4df0dcf6] __crystal_main +1734
[0x55fa4df6c086] *Crystal::main_user_code<Int32, Pointer(Pointer(UInt8))>:Nil +6
[0x55fa4df6bfe9] *Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32 +41
[0x55fa4df18776] main +6
[0x7f5b6ec5dce3] __libc_start_main +243
[0x55fa4df0d55e] _start +46
[0x0] ???
Reduced:
require "big"
10.to_big_i ** 10000000000
But **
just delegates to a gmp method. It consumes time and memory by (GMP's) design.
I'm not sure there's a problem to solve here.
the problem is e
, it 's easy to create a big big number, if we read a user form such as price or amount, and new with BigDecimal, is hard to validate before new, only use regex to test e
, and easy to forget to do.
should we have a 64bits version of Decimal, more time, we just want Decimal, not Big.
BigDecimal.new("1e1000000000000000000")
# gmp: overflow in mpz type
add more zero, it crash.
In Ruby:
> BigDecimal("1e10000000000000000000")
=> Infinity
Interesting... 🤔
But I don't know whether we can do the same. It's the same issue for BigInt
.
can we have some limitation for gmp, eg max value or max bits.
and limit to a low value, then if we really need big, increment it explicit.
there is no "attack" here, it's a developer self-destructing their code manually
happens in literally every language
@girng but ruby is ok
@girng no more suck.
The actual problem is that BigDecimal#scale
in Crystal is a UInt64
, so it can express 1e-10000000
very concisely (value == 1.to_big_i, scale == 10000000
), but not 1e+10000000
(value == 10.to_big_i ** 10000000, scale == 0
). Note that in the former case BigDecimal.new(String)
understands the exponent part and doesn't go through BigInt#**
.
I'm not sure why scale
is unsigned. We based our implementation on bigdecimal-rs, but their scale
has always been a signed quantity: https://github.com/akubera/bigdecimal-rs/blob/351411addce5018f6808e85ad7a3460993e81340/src/lib.rs#L173
block and cup 100% and memory 100%
ruby is ok