-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Fold modulo operator on constant values and raise zero remainder error quickly #2252
Conversation
3442d21
to
cbf2c22
Compare
cbf2c22
to
a60140b
Compare
Rebased to run tests. |
a60140b
to
45a0831
Compare
I also updated the constant folding tests. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe parts of your commit message could be a code comment on why infinity is returned?
@@ -230,6 +230,7 @@ static block constant_fold(block a, block b, int op) { | |||
case '-': res = jv_number(na - nb); break; | |||
case '*': res = jv_number(na * nb); break; | |||
case '/': res = jv_number(na / nb); break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to fix the division by zero here too. Just FYI.
Oh, hmm, maybe there's a better way to deal with division by zero / modulo zero in this function: if the divisor is zero then return a no-op block so that the caller will not constant-fold this so that the jq program can get a run-time error instead of infinity. Or maybe we should try to make this a compile-time error. We'd need some way for What do you think, @itchyny? |
I agree, EDIT: I didn't realise |
Also (maybe it does not matter much for constant folding since you can't have a constant nan, infinity, etc) things like |
Ahh wait, zero division is undefined behavior in C? I didn't know that. |
I didn't know either! ^^ I only learned it a few days ago because OSS Fuzz tried to compile I wasn't able to reproduce the UBSAN warning locally for some reason, but I looked at the C standard, and apparently division/modulo with 0 as rhs is unspecified behaviour for all types. |
Another alternative solution I like is to somehow export the |
My preference is that We could also then handle this as a compile-time error at the call-sites for |
@@ -230,6 +230,7 @@ static block constant_fold(block a, block b, int op) { | |||
case '-': res = jv_number(na - nb); break; | |||
case '*': res = jv_number(na * nb); break; | |||
case '/': res = jv_number(na / nb); break; | |||
case '%': res = jv_number((intmax_t)nb == 0 ? INFINITY : (intmax_t)na % (intmax_t)nb); break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about this instead:
diff --git a/src/parser.y b/src/parser.y
index 549f6f74..879774b4 100644
--- a/src/parser.y
+++ b/src/parser.y
@@ -230,7 +230,8 @@ static block constant_fold(block a, block b, int op) {
case '+': res = jv_number(na + nb); break;
case '-': res = jv_number(na - nb); break;
case '*': res = jv_number(na * nb); break;
- case '/': res = jv_number(na / nb); break;
+ case '/': if (nb == 0.0) return gen_noop(); res = jv_number(na / nb); break;
+ case '%': if (nb == 0.0) return gen_noop(); res = jv_number(na % nb); break;
case EQ: res = (cmp == 0 ? jv_true() : jv_false()); break;
case NEQ: res = (cmp != 0 ? jv_true() : jv_false()); break;
case '<': res = (cmp < 0 ? jv_true() : jv_false()); break;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This turns division of a literal by a literal zero into a run-time error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nicowilliams @itchyny Yes, I say let's do that and stop treating division by zero (expressions that return INFINITY) compile time errors; that was just confusing.
Then let's also revert #2253, and close #2780 since we don't actually want 0/0 to return NaN.
After that the OSS fuzz issue should be fixed, and costant-folding's behaviour will be aligned with f_divide
.
@itchyny Since you can't see it, the OSS fuzz issue is https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=60848 if you want to link it in the commit; the reproducer is jq '0/0'
(it doesn't trigger UBSAN on my amd64 PC though)
Currently constant division is folded on parsing and zero division raises
Division by zero?
error quickly. There isRemainder by zero?
error message found inparser.y
but this will never be thrown becauseconstant_fold
does not fold the modulo operator. So I added folding on constant modulo and makejq -n "1%0"
throws theRemainder by zero?
error and exits with3
(just likejq -n "1/0"
). The infinity is caught byblock_is_const_inf
. Returning infinity here may look wired (zero modulo is not infinity) but it applies to division as well; zero division in jq is not infinity but it's ok because the infinity is filtered out byblock_is_const_inf
. This follows the commit b9c2a32.