-
Notifications
You must be signed in to change notification settings - Fork 53
Description
After converting the c() function from the bc maths library to a "builtin" perl sub, I noticed the following illegal expression is accepted:
c(0.1, 0.2) + c()
The c() function requires one argument, but the functions are written to simply take the required arguments off an operand stack without much validation. I added trace statements to help debug this.
%git diff bc
diff --git a/bin/bc b/bin/bc
index b9c2c0f..3ec5cbc 100755
--- a/bin/bc
+++ b/bin/bc
@@ -2140,7 +2140,10 @@ sub bi_c
{
my $stack = shift;
+my $n = scalar @$stack;
+warn "DBG: bi_c() called with $n arguments\n";
my $val = pop @$stack;
+warn "DBG: ... using argument of $val\n";
die "c(n): missing argument\n" unless defined $val;
my $bignum = ref $val;
$val = $val->numify() if $bignum;
@@ -2314,9 +2317,13 @@ sub exec_stmt
$_ eq '<<_' or $_ eq '>>_' or $_ eq '||_' or $_ eq '&&_') {
# Binary operators
- my $b = pop(@ope_stack); my $a = pop(@ope_stack);
+ my $b = pop(@ope_stack);
+ my $a = pop(@ope_stack);
- if ($_ eq '+_') { $res = $a + $b ; 1 }
+ if ($_ eq '+_') {
+ warn "DBG: binop $a '+' $b\n";
+ $res = $a + $b;
+ }
elsif($_ eq '-_') { $res = $a - $b ; 1 }
elsif($_ eq '*_') { $res = $a * $b ; 1 }
elsif($_ eq '/_') { die 'divide by 0' if ($bignum ? $b->is_zero : $b == 0); $res = $a / $b }
%perl bc -l
c(0.1, 0.2) + c()
DBG: bi_c() called with 2 arguments
DBG: ... using argument of 0.2
DBG: bi_c() called with 2 arguments
DBG: ... using argument of 0.980066577841242
DBG: binop 0.1 '+' 0.556967252809642
0.656967252809642
quit
%
So the 0.1 argument is passed to neither instance of c(); instead it is taken last as an argument to the addition operator. The result of the 1st c() is taken as the argument to the 2nd c(). The result of the 2nd c() is added to 0.1, which is equivalent to:
%bc.gavin --version
bc.gavin 5.2.1
Copyright (c) 2018-2021 Gavin D. Howard and contributors
Report bugs at: https://git.yzena.com/gavin/bc
This is free software with ABSOLUTELY NO WARRANTY.
%bc.gavin -l -e 'c(c(0.2)) + 0.1'
.65696725280964238181
To solve this, I think bc would need to be aware of the number of arguments a function is actually passed as per argument-list (a,b,c). Flattening all argument lists into a single operand stack results in loss of the argument count per function. I have no current solution at this time.