Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crash in Java powInt function #261

Closed
mschessler opened this issue May 29, 2022 · 17 comments
Closed

Crash in Java powInt function #261

mschessler opened this issue May 29, 2022 · 17 comments
Labels

Comments

@mschessler
Copy link

MathParser.org-mXparser version: v.5.0.4
Framework: Java

Bug:

While fuzzing the library I found multiple inputs crashing it.
All of them have a syntax which passes checkSyntax().
The problem looks to be at org.mariuszgromada.math.mxparser.mathcollection.MathFunctions.powInt(MathFunctions.java:1050).
Take a look at the attached file.

A quick fix would be just catching the exception:

private static double powInt(double a, int n) {
		try {
			if (Double.isNaN(a)) return Double.NaN;
			if (Double.isInfinite(a)) Math.pow(a, n);
			if (a == 0) return Math.pow(a, n);
			if (n == 0) return 1;
			if (n == 1) return a;
			if (mXparser.checkIfCanonicalRounding()) {
				BigDecimal da = BigDecimal.valueOf(a);
				if (n >= 0) return da.pow(n).doubleValue();
				else return BigDecimal.ONE.divide(da, MathContext.DECIMAL128).pow(-n).doubleValue();
			} else {
				return Math.pow(a, n);
			}
		}catch (NumberFormatException | ArithmeticException e){
			return Double.NaN;
		}
}

Exceptions:
Crashes.txt

Just two random inputs which crash:
crash-1a92d270625b937901abb263be8bbd031cab9106.txt
crash-7ecf46c837d7aa976de9844ad1ce9e485b6d2248.txt

@mariuszgromada
Copy link
Owner

mariuszgromada commented May 29, 2022

Thanks. I will handle this.

@mariuszgromada
Copy link
Owner

Can you share your code? In my case I do to see an exception, just NaN

Expression e = new Expression("1^1!^2^^2!^^2!^^92^B32.252cerf^9292");
e.setVerboseMode();
double r =  e.calculate();
mXparser.consolePrintln(r);
[mXparser-v.5.0.4] 
[][1^1!^2^^2!^^2!^^92^B32.252cerf^9292] 
[][1^1!^2^^2!^^2!^^92^B32.252cerf^9292] Starting ...
[][1^1!^2^^2!^^2!^^92^B32.252cerf^9292] Starting calculation loop
[][1^1!^2^^2!^^2!^^92^B32.252cerf^9292] Parsing (0, 17)  ---> 1.0 ^ 1.0 ! ^ 2.0 ^^ 2.0 ! ^^ 2.0 ! ^^ 92.0 ^ 2.317761391E9 ^ 9292.0  ...  ---> 1.0 ^ 1.0 ! ^ 4.0 ! ^^ 2.0 ! ^^ 92.0 ^ 2.317761391E9 ^ 9292.0  ...  done
[][1^1!^2^^2!^^2!^^92^B32.252cerf^9292] Parsing (0, 15)  ---> 1.0 ^ 1.0 ! ^ 4.0 ! ^^ 2.0 ! ^^ 92.0 ^ 2.317761391E9 ^ 9292.0  ...  ---> 1.0 ^ 1.0 ! ^ 4.0 ! ^^ 2.0 ! ^^ 92.0 ^ Infinity  ...  done
[][1^1!^2^^2!^^2!^^92^B32.252cerf^9292] Parsing (0, 13)  ---> 1.0 ^ 1.0 ! ^ 4.0 ! ^^ 2.0 ! ^^ 92.0 ^ Infinity  ...  ---> 1.0 ^ 1.0 ! ^ 4.0 ! ^^ 2.0 ! ^^ Infinity  ...  done
[][1^1!^2^^2!^^2!^^92^B32.252cerf^9292] Parsing (0, 11)  ---> 1.0 ^ 1.0 ! ^ 4.0 ! ^^ 2.0 ! ^^ Infinity  ...  ---> 1.0 ! ^ 4.0 ! ^^ 2.0 ! ^^ Infinity  ...  done
[][1^1!^2^^2!^^2!^^92^B32.252cerf^9292] Parsing (0, 9)  ---> 1.0 ! ^ 4.0 ! ^^ 2.0 ! ^^ Infinity  ...  ---> 1.0 ^ 4.0 ! ^^ 2.0 ! ^^ Infinity  ...  done
[][1^1!^2^^2!^^2!^^92^B32.252cerf^9292] Parsing (0, 8)  ---> 1.0 ^ 4.0 ! ^^ 2.0 ! ^^ Infinity  ...  ---> 1.0 ! ^^ 2.0 ! ^^ Infinity  ...  done
[][1^1!^2^^2!^^2!^^92^B32.252cerf^9292] Parsing (0, 6)  ---> 1.0 ! ^^ 2.0 ! ^^ Infinity  ...  ---> 1.0 ^^ 2.0 ! ^^ Infinity  ...  done
[][1^1!^2^^2!^^2!^^92^B32.252cerf^9292] Parsing (0, 5)  ---> 1.0 ^^ 2.0 ! ^^ Infinity  ...  ---> 1.0 ! ^^ Infinity  ...  done
[][1^1!^2^^2!^^2!^^92^B32.252cerf^9292] Parsing (0, 3)  ---> 1.0 ! ^^ Infinity  ...  ---> 1.0 ^^ Infinity  ...  done
[][1^1!^2^^2!^^2!^^92^B32.252cerf^9292] Parsing (0, 2)  ---> 1.0 ^^ Infinity  ...  ---> NaN  ...  done
[][1^1!^2^^2!^^2!^^92^B32.252cerf^9292] Calculated value: NaN
[][1^1!^2^^2!^^2!^^92^B32.252cerf^9292] Exiting

NaN
[mXparser-v.5.0.4] 

@mariuszgromada
Copy link
Owner

mariuszgromada commented May 29, 2022

"^^" this a tetration operator - so syntax is fully ok

@mariuszgromada
Copy link
Owner

Also tokenizer did the job as expected, please note that "B32." prefix notifies parser that it is a base 32 number literal.

Expression e = new Expression("1^1!^2^^2!^^2!^^92^B32.252cerf^9292");
e.consolePrintCopyOfInitialTokens();
[mXparser-v.5.0.4]  --------------------
[mXparser-v.5.0.4] | Expression tokens: |
[mXparser-v.5.0.4]  ---------------------------------------------------------------------------------------------------------------
[mXparser-v.5.0.4] |    TokenIdx |       Token |        KeyW |     TokenId | TokenTypeId |  TokenLevel |  TokenValue |   LooksLike |
[mXparser-v.5.0.4]  ---------------------------------------------------------------------------------------------------------------
[mXparser-v.5.0.4] |           0 |           1 |       _num_ |           1 |           0 |           0 |         1.0 |             |
[mXparser-v.5.0.4] |           1 |           ^ |           ^ |           5 |           1 |           0 |         NaN |             |
[mXparser-v.5.0.4] |           2 |           1 |       _num_ |           1 |           0 |           0 |         1.0 |             |
[mXparser-v.5.0.4] |           3 |           ! |           ! |           6 |           1 |           0 |         NaN |             |
[mXparser-v.5.0.4] |           4 |           ^ |           ^ |           5 |           1 |           0 |         NaN |             |
[mXparser-v.5.0.4] |           5 |           2 |       _num_ |           1 |           0 |           0 |         2.0 |             |
[mXparser-v.5.0.4] |           6 |          ^^ |          ^^ |           9 |           1 |           0 |         NaN |             |
[mXparser-v.5.0.4] |           7 |           2 |       _num_ |           1 |           0 |           0 |         2.0 |             |
[mXparser-v.5.0.4] |           8 |           ! |           ! |           6 |           1 |           0 |         NaN |             |
[mXparser-v.5.0.4] |           9 |          ^^ |          ^^ |           9 |           1 |           0 |         NaN |             |
[mXparser-v.5.0.4] |          10 |           2 |       _num_ |           1 |           0 |           0 |         2.0 |             |
[mXparser-v.5.0.4] |          11 |           ! |           ! |           6 |           1 |           0 |         NaN |             |
[mXparser-v.5.0.4] |          12 |          ^^ |          ^^ |           9 |           1 |           0 |         NaN |             |
[mXparser-v.5.0.4] |          13 |          92 |       _num_ |           1 |           0 |           0 |        92.0 |             |
[mXparser-v.5.0.4] |          14 |           ^ |           ^ |           5 |           1 |           0 |         NaN |             |
[mXparser-v.5.0.4] |          15 | B32.252cerf |             |           1 |           0 |           0 | 2.317761391E9 |             |
[mXparser-v.5.0.4] |          16 |           ^ |           ^ |           5 |           1 |           0 |         NaN |             |
[mXparser-v.5.0.4] |          17 |        9292 |       _num_ |           1 |           0 |           0 |      9292.0 |             |
[mXparser-v.5.0.4]  ---------------------------------------------------------------------------------------------------------------

@mschessler
Copy link
Author

Sorry I forgot that I used the first bits of the input as Booleans for some of the libs options.

import org.mariuszgromada.math.mxparser.*;

public class Tester {

    public static void callApi(String input){
  
      Expression e = new Expression(input);
      e.calculate();
    }
  
    public static void main(String[] args){

        callApi("6^^6^^2.116^B18.08");
        
    }
}

crashes with:

Exception in thread "main" java.lang.NumberFormatException: Character I is neither a decimal digit number, decimal point, nor "e" notation exponential mark.
at java.base/java.math.BigDecimal.(BigDecimal.java:582)
at java.base/java.math.BigDecimal.(BigDecimal.java:467)
at java.base/java.math.BigDecimal.(BigDecimal.java:896)
at java.base/java.math.BigDecimal.valueOf(BigDecimal.java:1354)
at org.mariuszgromada.math.mxparser.mathcollection.MathFunctions.powInt(MathFunctions.java:1050)
at org.mariuszgromada.math.mxparser.mathcollection.MathFunctions.power(MathFunctions.java:1074)
at org.mariuszgromada.math.mxparser.Expression.POWER(Expression.java:2842)
at org.mariuszgromada.math.mxparser.Expression.calculate(Expression.java:6178)
at org.mariuszgromada.math.mxparser.Expression.calculate(Expression.java:5650)
at Tester.callApi(Tester.java:8)
at Tester.main(Tester.java:13)

Another Input: -3!<=3^2^9^92^9~&2
Crashes with:

Exception in thread "main" java.lang.ArithmeticException: Invalid operation
at java.base/java.math.BigDecimal.pow(BigDecimal.java:2468)
at org.mariuszgromada.math.mxparser.mathcollection.MathFunctions.powInt(MathFunctions.java:1051)
at org.mariuszgromada.math.mxparser.mathcollection.MathFunctions.power(MathFunctions.java:1074)
at org.mariuszgromada.math.mxparser.Expression.POWER(Expression.java:2842)
at org.mariuszgromada.math.mxparser.Expression.calculate(Expression.java:6178)
at org.mariuszgromada.math.mxparser.Expression.calculate(Expression.java:5650)
at Tester.callApi(Tester.java:8)
at Tester.main(Tester.java:13)

@mariuszgromada
Copy link
Owner

Ok!! Now I have the exception :-) Thanks! This is the first bug since many months :-) Great occasion to make the library better.

I will solve this very fast + I will make a release.

@mschessler
Copy link
Author

No problem. Part of a lab course at my University. I used Jazzer (https://github.com/CodeIntelligenceTesting/jazzer) for the fuzzing.

@mariuszgromada
Copy link
Owner

So, clear. BigDecimal.valueOf(double val) throws exception when double is NaN or Infinite (from doc "NumberFormatException – if val is infinite or NaN."). I do not like to solve problems by catching the exceptions, I will solve this differently.

@mariuszgromada
Copy link
Owner

A crazy bug :-) take a look
image

Can you please test?

Best regards

@mschessler
Copy link
Author

Should have seen that myself ^^
I will test.

@mariuszgromada
Copy link
Owner

The bug was only present in java code, C# code worked well :-)

@mariuszgromada
Copy link
Owner

Let me know when tested and confirmed - based on your confirmation I will make a release.

mariuszgromada added a commit that referenced this issue May 29, 2022
@mschessler
Copy link
Author

Ok so this exception seems fixed:

Exception in thread "main" java.lang.NumberFormatException: Character I is neither a decimal digit number, decimal point, nor "e" notation exponential mark.

There are still two more:

Input: callApi("-3!<=3^2^9^92^9~&2");

Crash:
[mXparser-v.5.0.5]
[][-3!<=3^2^9^92^9~&2]
[][-3!<=3^2^9^92^9~&2] Starting ...
[][-3!<=3^2^9^92^9~&2] Starting calculation loop
[][-3!<=3^2^9^92^9~&2] Parsing (0, 13) ---> -3.0 ! <= 3.0 ^ 2.0 ^ 9.0 ^ 92.0 ^ 9.0 ~& 2.0 ... ---> -3.0 ! <= 3.0 ^ 2.0 ^ 9.0 ^ 4.7216136328655667E17 & 2.0 ... done
[][-3!<=3^2^9^92^9
&2] Parsing (0, 11) ---> -3.0 ! <= 3.0 ^ 2.0 ^ 9.0 ^ 4.7216136328655667E17 ~& 2.0 ... Exception in thread "main" java.lang.ArithmeticException: Invalid operation
at java.base/java.math.BigDecimal.pow(BigDecimal.java:2468)
at org.mariuszgromada.math.mxparser.mathcollection.MathFunctions.powInt(MathFunctions.java:1067)
at org.mariuszgromada.math.mxparser.mathcollection.MathFunctions.power(MathFunctions.java:1090)
at org.mariuszgromada.math.mxparser.Expression.POWER(Expression.java:2858)
at org.mariuszgromada.math.mxparser.Expression.calculate(Expression.java:6194)
at org.mariuszgromada.math.mxparser.Expression.calculate(Expression.java:5666)
at Tester.callApi(Tester.java:9)
at Tester.main(Tester.java:14)

Input: callApi("2^2^^2^2^^2^2^^2222∛2^9^92^92^^0=2^9^92^9^2^2^9^92^92^^0=222^22^^2^9^9<--2^92^^0<2^9^92^9^2^2^9^92^92^^0=2^9^92^92");

Crash:
java.lang.ArithmeticException: Overflow
at java.base/java.math.BigDecimal.checkScale(BigDecimal.java:4377)
at java.base/java.math.BigDecimal.pow(BigDecimal.java:2471)
at org.mariuszgromada.math.mxparser.mathcollection.MathFunctions.powInt(MathFunctions.java:1067)
at org.mariuszgromada.math.mxparser.mathcollection.MathFunctions.power(MathFunctions.java:1090)
at org.mariuszgromada.math.mxparser.Expression.POWER(Expression.java:2858)
at org.mariuszgromada.math.mxparser.Expression.calculate(Expression.java:6194)
at org.mariuszgromada.math.mxparser.Expression.calculate(Expression.java:5666)
at Tester.callApi(Tester.java:10)
at Tester.main(Tester.java:16)

@mariuszgromada
Copy link
Owner

Thanks. It seems this this time I have to catch the exceptions as the number for BigDecimal pow is to big.

mariuszgromada added a commit that referenced this issue May 29, 2022
@mariuszgromada
Copy link
Owner

Can you test it now? Thanks :-)

@mschessler
Copy link
Author

Looks fine. No crashes after a 10min fuz run. Before there were some after like 10sek. I will let it run for some more hours but I think it is fixed.

@mariuszgromada
Copy link
Owner

Thanks, I did additional verification against all BiigDecimal in the code.

mariuszgromada added a commit that referenced this issue May 29, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants