Skip to content

Conversation

maximebuyse
Copy link
Collaborator

@maximebuyse maximebuyse commented Sep 24, 2025

This is a first step for proving panic freedom. Extraction using hax and F* type checking works, but some parts are admitted (with comments). The most common reasons for admits are:

The current state works with hax branch tls-codec-panic-freedom

Copy link
Member

@franziskuskiefer franziskuskiefer left a comment

Choose a reason for hiding this comment

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

Nice!

I think there are two different categories in here on overflow/underflow issues. This is definitely the typical class of issues in code like this. And I guess we didn't always review well.

  • When the function is returning an error anyway, these should really be caught and errors returned.
  • For non-failing functions it's a little tricky. Because we don't always want a Result. At the same time would it be nice to do better here. One easy option would be _checked variants that return a Result. Another option would be returning a tuple with a bool on whether the result is correct or not. Then the caller can decide what to do with it. This would be breaking though.

In some cases overflows may also not be possible and we may want to treat code differently, depending on whether that can happen or not.

/// The serialized len
#[inline(always)]
fn tls_serialized_byte_length(&$self) -> usize {
hax_lib::fstar!("admit ()"); // overflow
Copy link
Member

Choose a reason for hiding this comment

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

Yeah these kind of functions are difficult. We really don't want to return a Result but it's of course true that this may overflow.

In most cases this can't happen. But on a TlsVecU32 (or the slice version) on a 32 bit system this may indeed happen.

We could add a variant of the function with a _checked postfix and return a Result there.

// large and write it out.
let (tls_serialized_len, byte_length) = $self.get_content_lengths()?;

hax_lib::fstar!("admit ()"); // Need to prove unwrap for usize to U24
Copy link
Member

Choose a reason for hiding this comment

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

Maybe U24 should be implemented separately here to allow more checks?


#[inline]
fn index(&self, i: usize) -> &T {
hax_lib::fstar!("admit ()"); // index precondition
Copy link
Member

Choose a reason for hiding this comment

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

Similar to the length. Maybe we should have a checked version here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Here it is really the caller's responsibility to provide a valid index so it makes sense to have a precondition. I switched to this in the latest version.

fn from(value: U24) -> usize {
const LEN: usize = core::mem::size_of::<usize>();
let mut usize_bytes = [0u8; LEN];
hax_lib::assume!(LEN == 8); // https://github.com/cryspen/hax/issues/1702
Copy link
Member

Choose a reason for hiding this comment

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

This may also be 4 on 32 bit systems. Maybe we should just assume >= 4? We don't want to support smaller than 32 bit systems here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, I wanted to do LEN == 4 || LEN == 8. Unfortunately this doesn't work because of a hax bug (the issue that is linked in the comment).

@maximebuyse
Copy link
Collaborator Author

Nice!

I think there are two different categories in here on overflow/underflow issues. This is definitely the typical class of issues in code like this. And I guess we didn't always review well.

* When the function is returning an error anyway, these should really be caught and errors returned.

* For non-failing functions it's a little tricky. Because we don't always want a `Result`. At the same time would it be nice to do better here. One easy option would be `_checked` variants that return a `Result`. Another option would be returning a tuple with a `bool` on whether the result is correct or not. Then the caller can decide what to do with it. This would be breaking though.

In some cases overflows may also not be possible and we may want to treat code differently, depending on whether that can happen or not.

Thanks for the feedback. I'll have a look at the suggestions and push a new version with some more disruptive changes to fix some over/underflows.

@maximebuyse
Copy link
Collaborator Author

Nice!
I think there are two different categories in here on overflow/underflow issues. This is definitely the typical class of issues in code like this. And I guess we didn't always review well.

* When the function is returning an error anyway, these should really be caught and errors returned.

* For non-failing functions it's a little tricky. Because we don't always want a `Result`. At the same time would it be nice to do better here. One easy option would be `_checked` variants that return a `Result`. Another option would be returning a tuple with a `bool` on whether the result is correct or not. Then the caller can decide what to do with it. This would be breaking though.

In some cases overflows may also not be possible and we may want to treat code differently, depending on whether that can happen or not.

Thanks for the feedback. I'll have a look at the suggestions and push a new version with some more disruptive changes to fix some over/underflows.

I just pushed a new version where additions and subtractions are replaced by checked_add and checked_sub when they happen in a function that returns a Result. I mostly used Error::LibraryError in case of overflow, we might want to change that.
I also fixed a few other issues, so now we are down to 5 places with potential overflows (where we cannot return an error). We also have left the usize to U24 conversion issue, and the various issues that are caused by hax.

@maximebuyse
Copy link
Collaborator Author

@franziskuskiefer I made a try for having a checked version for tls_serialized_len alongside the unchecked version. Do you think this style would be ok? It solves the panic freedom issues we have been having but it is quite disruptive.

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.

2 participants