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

Fix field division by handling division by zero #969

Merged
merged 12 commits into from
Mar 5, 2025
Merged

Conversation

jotabulacios
Copy link
Contributor

Fix field division by handling division by zero

Description

This PR fixes a potential division by zero error in the div function for FieldElement which could cause a panic. Instead of performing direct division, the implementation now returns a Result<FieldElement<L>, FieldError>, ensuring error handling when division by zero occurs.

@codecov-commenter
Copy link

codecov-commenter commented Feb 4, 2025

Codecov Report

Attention: Patch coverage is 75.59809% with 51 lines in your changes missing coverage. Please review.

Project coverage is 71.66%. Comparing base (7e94a19) to head (d4c9e5c).

Files with missing lines Patch % Lines
math/src/field/fields/mersenne31/extensions.rs 31.57% 13 Missing ⚠️
.../src/field/fields/fft_friendly/quartic_babybear.rs 0.00% 9 Missing ⚠️
...rt_weierstrass/curves/bls12_377/field_extension.rs 11.11% 8 Missing ⚠️
...short_weierstrass/curves/bn_254/field_extension.rs 11.11% 8 Missing ⚠️
.../field/fields/fft_friendly/quartic_babybear_u32.rs 0.00% 6 Missing ⚠️
math/src/field/test_fields/u32_test_field.rs 0.00% 3 Missing ⚠️
math/src/field/test_fields/u64_test_field.rs 0.00% 3 Missing ⚠️
math/src/elliptic_curve/short_weierstrass/point.rs 90.90% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #969      +/-   ##
==========================================
+ Coverage   71.65%   71.66%   +0.01%     
==========================================
  Files         156      156              
  Lines       34235    34316      +81     
==========================================
+ Hits        24531    24593      +62     
- Misses       9704     9723      +19     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@jotabulacios jotabulacios marked this pull request as ready for review February 4, 2025 16:19
@jotabulacios jotabulacios requested a review from a team as a code owner February 4, 2025 16:19
Copy link
Contributor

@Oppen Oppen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot to flush the review, sorry.

Comment on lines 358 to 360
} else if E::defining_equation(unsafe { &(&x / &z).unwrap_unchecked() }, unsafe {
&(&y / &z).unwrap_unchecked()
}) == FieldElement::zero()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably use regular unwrap here.
It may be useful to compute the inverse of z only once (not necessary to do now) and multiply later as well.

Comment on lines 350 to 365
if z == FieldElement::zero() {
let point = Self::new([x, y, z])
.map_err(|_| DeserializationError::FieldFromBytesError)?;
if point.is_neutral_element() {
Ok(point)
} else {
Err(DeserializationError::FieldFromBytesError)
}
} else if E::defining_equation(unsafe { &(&x / &z).unwrap_unchecked() }, unsafe {
&(&y / &z).unwrap_unchecked()
}) == FieldElement::zero()
{
Self::new([x, y, z]).map_err(|_| DeserializationError::FieldFromBytesError)
} else {
Err(DeserializationError::FieldFromBytesError)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think something like this might be cleaner (assuming inv gives us the inverse and returns a Result<_>:

Suggested change
if z == FieldElement::zero() {
let point = Self::new([x, y, z])
.map_err(|_| DeserializationError::FieldFromBytesError)?;
if point.is_neutral_element() {
Ok(point)
} else {
Err(DeserializationError::FieldFromBytesError)
}
} else if E::defining_equation(unsafe { &(&x / &z).unwrap_unchecked() }, unsafe {
&(&y / &z).unwrap_unchecked()
}) == FieldElement::zero()
{
Self::new([x, y, z]).map_err(|_| DeserializationError::FieldFromBytesError)
} else {
Err(DeserializationError::FieldFromBytesError)
}
let Some(z_inv) = z.inv() else {
let point = Self::new([x, y, z])
.map_err(|_| DeserializationError::FieldFromBytesError)?;
return if point.is_neutral_element() {
Ok(point)
} else {
Err(DeserializationError::FieldFromBytesError)
};
};
if E::defining_equation(&(&x * &z_inv), &(&y * &z_inv)) == FieldElement::zero()
{
Self::new([x, y, z]).map_err(|_| DeserializationError::FieldFromBytesError)
} else {
Err(DeserializationError::FieldFromBytesError)
}

This version has no unwraps and yet exploits that past the inverse calculation we know z != 0.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solved in 6f0e40d

Comment on lines +370 to +371
.map_err(|_| ProverError::DivisionByZero)
.unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the intent here was to remove the unwrap:

Suggested change
.map_err(|_| ProverError::DivisionByZero)
.unwrap();
.map_err(|_| ProverError::DivisionByZero)?;

@jotabulacios jotabulacios requested a review from Oppen March 5, 2025 18:26
@diegokingston diegokingston added this pull request to the merge queue Mar 5, 2025
Merged via the queue into main with commit be4a329 Mar 5, 2025
8 checks passed
@diegokingston diegokingston deleted the fix-field-div branch March 5, 2025 20:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants