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

Add checked arithmetic operations traits #298

Conversation

ncatelli
Copy link

@ncatelli ncatelli commented Aug 19, 2021

General

This is an initial attempt to define a CheckedAdd and CheckedSub set of traits per the traits discussed in the linked issue below. I've applied these traits to the VirtAddr addressing types listed in the issue to check address, page and frame arithmetic.

Related Issues

#293

.gitignore Outdated Show resolved Hide resolved
src/addr.rs Outdated Show resolved Hide resolved
@ncatelli ncatelli force-pushed the expermiment/293/add-checked-arithmetic-operations-traits branch from 137cdef to 76ca2e9 Compare August 19, 2021 20:24
src/lib.rs Show resolved Hide resolved
Copy link
Contributor

@josephlr josephlr left a comment

Choose a reason for hiding this comment

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

Thanks for this PR. My comments are mostly doc/implementation nits.

Note that while #299 makes some of the implementation tricky. I don't think we have to wait on that bug to be fixed before submitting these changes.

.gitignore Outdated Show resolved Hide resolved
src/ops.rs Outdated Show resolved Hide resolved
src/ops.rs Outdated Show resolved Hide resolved
src/ops.rs Outdated Show resolved Hide resolved
src/ops.rs Outdated Show resolved Hide resolved
src/addr.rs Outdated Show resolved Hide resolved
src/addr.rs Outdated Show resolved Hide resolved
src/addr.rs Outdated Show resolved Hide resolved
src/lib.rs Show resolved Hide resolved
src/ops.rs Outdated Show resolved Hide resolved
@ncatelli
Copy link
Author

ncatelli commented Aug 20, 2021

I've made all requested changes and have mostly stuck to your requests directly, the only difference would be retention of the chain in the VirtAddr::checked_* methods due to the from_canonical change keeping the chain fairly readable.

@josephlr
Copy link
Contributor

@ncatelli I've resolved all the comments that you've fixed (I think there are still a few more comment nits to fixed).

Can you add implementations for the other address types (PhysAddr, PhysFrame, Page)

@josephlr
Copy link
Contributor

Also @ncatelli per #293 (comment) can you remove the usize implementations here.

Copy link
Author

@ncatelli ncatelli left a comment

Choose a reason for hiding this comment

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

Sorry about that. I had over looked the other changes you'd requested, these are now fixed.

I've implemented the trait for the additional types you requested for the types laid out in #293. I've left a few comments regarding preference questions on a few of the check calls. Since most of these questions apply to the implementations of both Page and PhysFrame, I've only left discussion comments on PhysFrame.

Again, thanks again for all the time, I know this PR and attached issue have been fairly high-touch.

@@ -16,7 +17,7 @@ pub struct PhysFrame<S: PageSize = Size4KiB> {
}

impl<S: PageSize> PhysFrame<S> {
/// Returns the frame that starts at the given virtual address.
/// Returns the frame that starts at the given physical address.
Copy link
Author

@ncatelli ncatelli Aug 22, 2021

Choose a reason for hiding this comment

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

Small typo that I'd noticed and corrected. I just wanted to call it out explicitly since this was beyond the scope of the PR and just to spot-check my change

@@ -28,7 +29,7 @@ impl<S: PageSize> PhysFrame<S> {
}

const_fn! {
/// Returns the frame that starts at the given virtual address.
/// Returns the frame that starts at the given physical address.
Copy link
Author

Choose a reason for hiding this comment

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

Same as above, additional doc change.

fn checked_sub(self, rhs: PhysFrame<S>) -> Option<Self::Output> {
self.start_address()
.checked_sub(rhs.start_address())
.map(PhysAddr::new)
Copy link
Author

Choose a reason for hiding this comment

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

I opted for new, which can panic over try_new as the sub should prevent ever falling into panic inducing address space unless someone is doing something really bananas. I can happily change this to try_new if you would prefer since new just calls try_new under the hood. This would guarantee we entirely avoid a panic that I think won't ever even happen, but at the expense of denser code.

Copy link
Author

Choose a reason for hiding this comment

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

Nevermind. The more i thought about this I opted to change it. The guarantee of no panic for a checked_* method should probably be the default.

Copy link
Member

@Freax13 Freax13 Dec 3, 2021

Choose a reason for hiding this comment

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

What is the point of converting the offset to a PhysAddr and PhysFrame? Couldn't you just write this?

self.start_address()
    .checked_sub(rhs.start_address())
    .map(|offset| offset / S::SIZE)

Copy link
Author

Choose a reason for hiding this comment

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

Hello, I opted for the case of safely validating of the address and the alignment as there are a few cases where an unaligned frame start address could be returned. This would necessitate the caller broke the contract of from_start_address_unchecked. I'm happy to change this method if you feel strongly about it.

Copy link
Member

Choose a reason for hiding this comment

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

Because from_start_unchecked is marked as unsafe, we can always assume that the caller did not violate the safety requirements and we don't need to check for them again.

Even if the caller did violate the requirements, this would cause the function to fail as if an underflow occurred even when that's not the case, which seems a bit unexpected.

Copy link
Author

Choose a reason for hiding this comment

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

Fair, the change you've requested has been made.

Copy link
Member

Choose a reason for hiding this comment

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

I noticed that you also used this pattern in a few other places, those probably need to be changed too.

Copy link
Author

Choose a reason for hiding this comment

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

Applied the same change to Page::checked_sub to match the same. By other's i assume you mean something like https://github.com/rust-osdev/x86_64/pull/298/files#diff-d2a15219d98ad66c2aeac0ee03d682034bc619fe5905e63960599aa58b7a63cfR332-R342, in which this was done for the sake of checking valid addresses of the multiple and I think there is no reason to change that.

Copy link
Member

Choose a reason for hiding this comment

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

You don't need to check if the offset is a valid address, in fact it is wrong to do so. For addition it's perfectly fine to jump from the lower half of the address space to the upper half. In that case the offset is not necessarily a valid virtual address. Offsets just aren't the same as virtual (or physical) addresses.

I'm aware that there has been some discussion on whether we should allow jumping from the lower half to the upper half and I'm open to change in that direction. However for now we should make sure that the checked and non-checked implementations behave in the same way.

fn checked_add(self, rhs: u64) -> Option<Self::Output> {
let phys_addr_rhs = rhs
.checked_mul(S::SIZE)
.and_then(|addr| PhysAddr::try_new(addr).ok())?;
Copy link
Author

Choose a reason for hiding this comment

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

Let me know if this is overkill and you'd like me to opt for a lighter weight checked op by removing the PhysAddr validity check.

@ncatelli
Copy link
Author

Alright, that should be the last of my changes until the next review cycle. I wanted to make sure intent was either clear or documented with examples (that hopefully make it clear).

@ncatelli
Copy link
Author

ncatelli commented Aug 24, 2021

One additional note, while I'm aware of the discussion in #293 about removing the usize impls for Add and Sub this PR doesn't currently contain that since it was still under discussion, and I'd assumed out of scope. I'd be happy to add that or more preferably provide a follow up PR with those changes. Additionally I'm working off the assumption that #299 and the corresponding jumping_add method/trait fell out of scope of this PR.

@ncatelli ncatelli requested a review from josephlr September 8, 2021 22:55
@phil-opp phil-opp added the waiting-for-review Waiting for a review from the maintainers. label Nov 6, 2021
@phil-opp
Copy link
Member

phil-opp commented Nov 6, 2021

Triage: This PR has been waiting for some time. @josephlr Do you have time for a new review, or should I try to take over?

@ncatelli ncatelli force-pushed the expermiment/293/add-checked-arithmetic-operations-traits branch from 4d95a3a to ad70df8 Compare November 6, 2021 20:03
@ncatelli ncatelli closed this Aug 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
waiting-for-review Waiting for a review from the maintainers.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants