Skip to content

Linter PR 6: eliminate usage of unsafe #653

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

Open
wants to merge 83 commits into
base: main
Choose a base branch
from

Conversation

faddat
Copy link
Contributor

@faddat faddat commented Apr 22, 2025

This pull request makes the use of unsafe code much less common in wasmvm, and makes a number of other tightenings and enhancements.

Chat log from chat with Skip/ICL

I've made a memory safe unmanaged vector
3:57
I know lots of people said it is impossible, but well, that's done and that would get us huge breathing room for new implementations of all kinds of things
3:58
like IBCPacket recieve
3:58
I just fixed the unmanaged vector issues there.
3:58
it now uses SafeUnmanagedVerctor
3:59
this is like, unambiguously good
4:00
Key safety features of SafeUnmanagedVector:
It tracks whether the vector has been consumed with a consumed boolean flag
The consume() method checks this flag and returns an error if the vector has already been consumed
It provides safer methods like is_consumed() to check the consumption state
It has helper methods to safely create, consume, and destroy vectors
The FFI interface provides functions like:
new_safe_unmanaged_vector: Creates a SafeUnmanagedVector that tracks consumption
destroy_safe_unmanaged_vector: Safely destroys a SafeUnmanagedVector, preventing double-free
safe_unmanaged_vector_is_none: Checks if a vector is None without consuming it
safe_unmanaged_vector_length: Gets vector length safely
safe_unmanaged_vector_to_bytes: Copies content to a Go byte slice
This implementation provides a much safer alternative to the original UnmanagedVector by preventing common memory safety issues like double-free that can occur when working with FFI between Rust and Go.


Jacob Gadikian
  4:07 PM
Updated all IBC-related functions to use CopyAndDestroyToSafeVector instead of copyAndDestroyUnmanagedVector:
IBCChannelOpen, IBCChannelConnect, IBCChannelClose
IBCPacketReceive, IBC2PacketReceive, IBCPacketAck, IBCPacketTimeout
IBCSourceCallback, IBCDestinationCallback
Updated error handling in StoreCode, StoreCodeUnchecked, and errorWithMessage to use the safer approach.
Updated analysis and metrics functions to use the safer approach:
receiveAnalysisReport - Now safely handles required_capabilities and entrypoints
receivePinnedMetrics - Now uses SafeUnmanagedVector for the metrics data
4:07
now these would really solve serious security issues.
4:07
These changes make the codebase more consistent in its approach to memory management. The safer patterns help prevent:
Memory leaks - By ensuring proper cleanup of allocated memory
Double-free issues - By tracking whether a vector has been consumed already
Use-after-free bugs - By preventing access to memory after it has been freed
4:08
all of that started just with linting rigorously


Jacob Gadikian
  4:17 PM
The bug has been fixed by replacing the direct json.Unmarshal() calls with DeserializeResponse() in the Instantiate, Execute, and Query methods.
The fix ensures that:
Gas costs are properly accounted for when deserializing response data
Gas is charged based on the size of the response
Gas reports are properly updated
This prevents the potential DoS vulnerability that could have been exploited by returning large responses without incurring the correct computational cost.
4:17
etc etc etc etc
4:18
In the UnmarshalJSON method for VoteMsg, a nil pointer dereference can occur if both "option" and "vote" fields are missing in the input JSON. When tmp.Option remains nil, attempting to assign *tmp.Option to Option will cause a panic. The method should add a check to handle cases where neither field is provided, either by returning an error or setting a default value.
4:19
I could be wrong but that could be a take-out-any-chain issue
4:20
The Close() method of memDBIterator introduces a potential resource leak by asynchronously draining the channel. By spawning a separate goroutine to drain the channel, Close() returns immediately before the underlying traversal goroutine completes, which may cause the database's read lock to remain held longer than expected. This can potentially lead to resource leaks or deadlocks if callers assume Close() synchronously releases all resources.
...etc etc etc
4:21
Ricardo will be reaching out to figure out billing or something.  This needs to happen -- not just getting paid, but these improvements.  Wasmvm is just much too uncool right now, but it is savable
4:21
and then we can rewrite it
4:22
I just fixed all 3 of those
4:23
this is my attempting to show good faith here - I just want to get this stuff fixed so we can go on and moon without explosions theres been too many

faddat added 13 commits April 22, 2025 13:46
- Replaced unsafe calls to `go_error.into_result` with `go_error.into_result_safe` in `api.rs` and `storage.rs` to enhance safety and eliminate unsafe blocks.
- Removed `SafeUnmanagedVector` from imports and code where it was unnecessary.
- Added a new method `into_result_safe` in `GoError` to ensure safe error handling without risking reuse of error messages.
- Updated `GoIter` and `GoQuerier` implementations to replace unsafe calls to `go_result.into_result` with `go_result.into_result_safe`, enhancing safety in error handling.
- This change improves the overall robustness of the Go API by eliminating unsafe blocks in the iterator and querier modules.
- Modified error messages in `iterator_test.go` and `lib_test.go` to provide more specific feedback regarding gas limit and checksum format issues.
- Enhanced clarity of error reporting to improve debugging and user experience.
…king

- Introduced a new example in `debug_vectors.go` to demonstrate vector debugging capabilities.
- Enhanced `SafeUnmanagedVector` to track consumption state and provide detailed debug information, including stack traces for consumption attempts.
- Added functions to enable vector debugging and retrieve vector creation/consumption statistics.
- Updated error handling in `StoreCode` and `StoreCodeUnchecked` to ensure proper validation and error messaging.
@faddat faddat changed the title begin using deny.toml Linter PR 6: eliminate usage of unsafe Apr 22, 2025
faddat added 11 commits April 22, 2025 15:59
… for safer memory management

- Updated comments to clarify the purpose of contract functions.
- Replaced instances of copyAndDestroyUnmanagedVector with the safer CopyAndDestroyToSafeVector pattern across multiple contract functions, enhancing memory safety.
- Introduced a new receiveVectorSafe function to handle UnmanagedVector safely, preventing potential double-free issues.
…or improved memory safety

- Replaced all instances of copyAndDestroyUnmanagedVector with CopyAndDestroyToSafeVector in contract functions to enhance memory management.
- Updated comments to reflect the safer pattern being implemented across the codebase.
- Updated test cases to replace instances of copyAndDestroyUnmanagedVector with CopyAndDestroyToSafeVector, ensuring consistent use of safer memory management practices.
- Enhanced comments to clarify the safer approach being implemented in the tests.
- Updated the `Instantiate`, `Execute`, and `Query` methods to use a default `deserCost` value of 1/10000 gas per byte as defined in the VMConfig.
- Replaced direct JSON unmarshalling with `DeserializeResponse` to account for gas costs in the contract function implementations.
- Improved comments to clarify the changes made for gas cost management.
…d unmanaged vector functions

- Introduced `is_available` method in `SafeByteSlice` to check if the byte slice is not consumed and not nil, enhancing defensive programming.
- Added early return in `destroy_unmanaged_vector` to avoid unnecessary consumption of a nil vector.
- Implemented early checks in `safe_unmanaged_vector_to_bytes` to prevent consuming already consumed vectors.
- Updated test assertions for clarity and conciseness in error handling for consumed vectors.
- Introduced #[allow(non_camel_case_types)] attribute to the cache_t struct to suppress warnings related to naming conventions, improving code clarity and compliance with Go's conventions.
@faddat faddat mentioned this pull request Apr 22, 2025
Copy link
Collaborator

@chipshort chipshort left a comment

Choose a reason for hiding this comment

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

I will not review a 7000+ additions PR that's a total mess of all kinds of changes. I simply do not have time for that. Please keep your PRs separate unless it's absolutely necessary for them to depend on each other.
Looking at some of the functions you wrote here, I am not convinced this is as big of an improvement as you make it out to be, so I will not review further until there is a minimal PR with only the necessary changes for your SafeUnmanagedVector idea.

Comment on lines -315 to +617
let _ = v.consume();
// Wrap in SafeUnmanagedVector for safer handling
let mut safe_vector = SafeUnmanagedVector {
inner: v,
consumed: false,
};

// If the vector is None, we don't need to consume it
if safe_vector.inner.is_none() {
return;
}

// This will prevent double consumption by setting consumed flag
// and returning an error if already consumed
if let Err(e) = safe_vector.consume() {
// Log error but don't crash - better than double free
eprintln!("Error during vector destruction: {}", e);
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is complete nonsense and doesn't make this safer at all. You start out by creating a new SafeUnmanagedVector with consumed set to false, so your whole codepath just boils down to calling safe_vector.inner.consume() anyways, just with a lot of unnecessary fluff around it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this is what I needed!

thank you!

Comment on lines +153 to +154
safeVec := CopyAndDestroyToSafeVector(errmsg)
errMsg := string(safeVec.ToBytesAndDestroy())
Copy link
Collaborator

Choose a reason for hiding this comment

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

None of the uses of CopyAndDestroyToSafeVector make sense here. You always just call ToBytesAndDestroy immediately anyways. In that case, you might as well just use the existing copyAndDestroyUnmanagedVector directly, which returns perfectly safe []byte to you.
Using CopyAndDestroyToSafeVector gives no additional safety at all. Nothing stops you from calling it twice and therefore double-freeing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was worried about this.

@faddat
Copy link
Contributor Author

faddat commented Apr 25, 2025

hey @pinosu -- to be clear, this is exactly the kind of guidance I needed!

I will look into the rest, no worries, but I also want to provide you with some guidance -- the critical-path PR, that I must ask that you review is #589 , and reviewing that will greatly reduce the scope of others.

But in the interests of getting things done, and because I know the dangers of giant crazy PRs I'm going to break that down into other PRs, which work exactly linter per linter. But pelase, treat 589 as the critical one because we need to ensure that we don't do stupid things, that's really all the linter does.

I found a lot of them but that's no problem everyone does stupid things from time to time just please don't take it as an affront because it isn't meant that way.

@faddat
Copy link
Contributor Author

faddat commented Apr 25, 2025

the worst part is that on second thought you really may want to review this PR.

Tangled up in my linting I found a lot of stuff that's definitely a bug.

@faddat
Copy link
Contributor Author

faddat commented Apr 25, 2025

Sorry, input-validation 2

PLEASE READ IT IT CONTAINS THINGS THAT ARE BAD THANK YOU LAST FEW COMMITS

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