-
Notifications
You must be signed in to change notification settings - Fork 232
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
Object.lower() can result in use-after-free errors #1797
Comments
Changed the FFI for lowering objects avoid the scenario layed out in the issue. The new system is that the foreign code calls `inc_ref` on the handle, then the Rust code calls `remove` on it. This effectively makes it so `lower` returns an owned handle instead of a borrowed one. This replaces a lot of Kotlin code that was concerned about the same issue in a similar situation. Removed the `test_handlerace.kts` test for this, I believe this is now all handled at a lower level and we don't need this test. FWIW, the test was failing, but just because we now raise a different exception -- and the new exception mentions that you may be using the handle after it's free.
Changed the FFI for lowering objects avoid the scenario layed out in the issue. The new system is that the foreign code calls `inc_ref` on the handle, then the Rust code calls `remove` on it. This effectively makes it so `lower` returns an owned handle instead of a borrowed one. This replaces a lot of Kotlin code that was concerned about the same issue in a similar situation. Removed the `test_handlerace.kts` test for this, I believe this is now all handled at a lower level and we don't need this test. FWIW, the test was failing, but just because we now raise a different exception -- and the new exception mentions that you may be using the handle after it's free.
Changed the FFI for lowering objects avoid the scenario layed out in the issue. The new system is that the foreign code calls `inc_ref` on the handle, then the Rust code calls `remove` on it. This effectively makes it so `lower` returns an owned handle instead of a borrowed one. This replaces a lot of Kotlin code that was concerned about the same issue in a similar situation. Removed the `test_handlerace.kts` test for this, I believe this is now all handled at a lower level and we don't need this test. FWIW, the test was failing, but just because we now raise a different exception -- and the new exception mentions that you may be using the handle after it's free.
Changed the FFI for lowering objects avoid the scenario layed out in the issue. The new system is that the foreign code calls `inc_ref` on the handle, then the Rust code calls `remove` on it. This effectively makes it so `lower` returns an owned handle instead of a borrowed one. This replaces a lot of Kotlin code that was concerned about the same issue in a similar situation. Removed the `test_handlerace.kts` test for this, I believe this is now all handled at a lower level and we don't need this test. FWIW, the test was failing, but just because we now raise a different exception -- and the new exception mentions that you may be using the handle after it's free.
Changed the FFI for lowering objects avoid the scenario layed out in the issue. The new system is that the foreign code calls `inc_ref` on the handle, then the Rust code calls `remove` on it. This effectively makes it so `lower` returns an owned handle instead of a borrowed one. This replaces a lot of Kotlin code that was concerned about the same issue in a similar situation. Removed the `test_handlerace.kts` test for this, I believe this is now all handled at a lower level and we don't need this test. FWIW, the test was failing, but just because we now raise a different exception -- and the new exception mentions that you may be using the handle after it's free.
Changed the FFI for lowering objects avoid the scenario layed out in the issue. The new system is that the foreign code calls `inc_ref` on the handle, then the Rust code calls `remove` on it. This effectively makes it so `lower` returns an owned handle instead of a borrowed one. This replaces a lot of Kotlin code that was concerned about the same issue in a similar situation. Removed the `test_handlerace.kts` test for this, I believe this is now all handled at a lower level and we don't need this test. FWIW, the test was failing, but just because we now raise a different exception -- and the new exception mentions that you may be using the handle after it's free.
Changed the FFI for lowering objects avoid the scenario layed out in the issue. The new system is that the foreign code calls `inc_ref` on the handle, then the Rust code calls `remove` on it. This effectively makes it so `lower` returns an owned handle instead of a borrowed one. This replaces a lot of Kotlin code that was concerned about the same issue in a similar situation. Removed the `test_handlerace.kts` test for this, I believe this is now all handled at a lower level and we don't need this test. FWIW, the test was failing, but just because we now raise a different exception -- and the new exception mentions that you may be using the handle after it's free.
I think the general system we should use is that object handles are passed as borrows and returned as owned values. In this case, we should be cloning the handle before returning it. This seems pretty intuitive to me. In Rust you can pass a reference in without any special handling, but returning a reference requires lifetime checks. You can't check the lifetime of an object on the other side of the FFI, so the rule has to be any handle that's returned is an owned value. |
I think the above comment represents the ideal solution, but implementing that at this point would be quite complex. We could split I'm going to go with the simpler solution: always clone the reference in |
I want to fix mozilla#1797 by cloning the object handle in lower(). However, this presents an issue in the dynamic languages because they perform various failable checks in lower(): type checks, bounds checks, etc. If the check fails, then the cloned handle will be leaked. Prevent this by adding a `check()` method. We do the check first then once we get to `lower()`, nothing can fail.
Currently, `lower()` always returns a borrow of the object handle. This is fine for function arguments, since you know the object is still alive on the stack while the function is being called. However, for function returns this is not correct. To fix this: clone the handle in `lower()`. Added a test for this -- it was surprisingly easy to cause a segfault with the current behavior.
Now object handles are always cloned before they're lowered, then consumed on the Rust side. This makes it so `lower` returns an owned handle instead of a borrowed one.
Currently, `lower()` always returns a borrow of the object handle. This is fine for function arguments, since you know the object is still alive on the stack while the function is being called. However, for function returns this is not correct. To fix this: clone the handle in `lower()`. Added a test for this -- it was surprisingly easy to cause a segfault with the current behavior.
I want to fix mozilla#1797 by cloning the object handle in lower(). However, this presents an issue in the dynamic languages because they perform various failable checks in lower(): type checks, bounds checks, etc. If the check fails, then the cloned handle will be leaked. Prevent this by adding a `check()` method. We do the check first then once we get to `lower()`, nothing can fail.
Currently, `lower()` always returns a borrow of the object handle. This is fine for function arguments, since you know the object is still alive on the stack while the function is being called. However, for function returns this is not correct. To fix this: clone the handle in `lower()`. Added a test for this -- it was surprisingly easy to cause a segfault with the current behavior.
I want to fix mozilla#1797 by cloning the object handle in lower(). However, this presents an issue in the dynamic languages because they perform various failable checks in lower(): type checks, bounds checks, etc. If the check fails, then the cloned handle will be leaked. Prevent this by adding a `check()` method. We do the check first then once we get to `lower()`, nothing can fail.
Currently, `lower()` always returns a borrow of the object handle. This is fine for function arguments, since you know the object is still alive on the stack while the function is being called. However, for function returns this is not correct. To fix this: clone the handle in `lower()`. Added a test for this -- it was surprisingly easy to cause a segfault with the current behavior.
I want to fix mozilla#1797 by cloning the object handle in lower(). However, this presents an issue in the dynamic languages because they perform various failable checks in lower(): type checks, bounds checks, etc. If the check fails, then the cloned handle will be leaked. Prevent this by adding a `check()` method. We do the check first then once we get to `lower()`, nothing can fail.
Currently, `lower()` always returns a borrow of the object handle. This is fine for function arguments, since you know the object is still alive on the stack while the function is being called. However, for function returns this is not correct. To fix this: clone the handle in `lower()`. Added a test for this -- it was surprisingly easy to cause a segfault with the current behavior.
I want to fix mozilla#1797 by cloning the object handle in lower(). However, this presents an issue in the dynamic languages because they perform various failable checks in lower(): type checks, bounds checks, etc. If the check fails, then the cloned handle will be leaked. Prevent this by adding a `check()` method. We do the check first then once we get to `lower()`, nothing can fail.
Currently, `lower()` always returns a borrow of the object handle. This is fine for function arguments, since you know the object is still alive on the stack while the function is being called. However, for function returns this is not correct. To fix this: clone the handle in `lower()`. Added a test for this -- it was surprisingly easy to cause a segfault with the current behavior.
I'm trying to understand your proposed solution. What would be the ownership of handles in case of tail-returning foreign handle? If foreign bindings transfer ownership of foreign handle to Rust, then its up to Rust to free the foreign handle. If foreign bindings borrow foreign handle to Rust, then Rust doesn't have to free the handle, and foreign bindings will free the handle after function call. However, borrowing means the handle will go out of scope as soon as Rust function exits, so the handle can't be retained for any longer for callback-like use cases. How does cloning the handle in
To me it sounds like what is actually needed here is to dedicate one handle bit to signal that its a tail-handle, and should be freed when lifting by foreign bindings themselves. This would create an option for Rust to conditionally either free the handle in the more usual case, or omit freeing the handle and instead set the tail bit for the handle. I'm not entirely sure how practical it would be detect this type of tail-return. |
I want to fix mozilla#1797 by cloning the object handle in lower(). However, this presents an issue in the dynamic languages because they perform various failable checks in lower(): type checks, bounds checks, etc. If the check fails, then the cloned handle will be leaked. Prevent this by adding a `check()` method. We do the check first then once we get to `lower()`, nothing can fail.
Currently, `lower()` always returns a borrow of the object handle. This is fine for function arguments, since you know the object is still alive on the stack while the function is being called. However, for function returns this is not correct. To fix this: clone the handle in `lower()`. Added a test for this -- it was surprisingly easy to cause a segfault with the current behavior.
I want to fix mozilla#1797 by cloning the object handle in lower(). However, this presents an issue in the dynamic languages because they perform various failable checks in lower(): type checks, bounds checks, etc. If the check fails, then the cloned handle will be leaked. Prevent this by adding a `check()` method. We do the check first then once we get to `lower()`, nothing can fail.
Currently, `lower()` always returns a borrow of the object handle. This is fine for function arguments, since you know the object is still alive on the stack while the function is being called. However, for function returns this is not correct. To fix this: clone the handle in `lower()`. Added a test for this -- it was surprisingly easy to cause a segfault with the current behavior.
I want to fix mozilla#1797 by cloning the object handle in lower(). However, this presents an issue in the dynamic languages because they perform various failable checks in lower(): type checks, bounds checks, etc. If the check fails, then the cloned handle will be leaked. Prevent this by adding a `check()` method. We do the check first then once we get to `lower()`, nothing can fail.
Currently, `lower()` always returns a borrow of the object handle. This is fine for function arguments, since you know the object is still alive on the stack while the function is being called. However, for function returns this is not correct. To fix this: clone the handle in `lower()`. Added a test for this -- it was surprisingly easy to cause a segfault with the current behavior.
Currently, `lower()` always returns a borrow of the object handle. This is fine for function arguments, since you know the object is still alive on the stack while the function is being called. However, for function returns this is not correct. To fix this: clone the handle in `lower()`. Added a test for this -- it was surprisingly easy to cause a segfault with the current behavior.
I want to fix mozilla#1797 by cloning the object handle in lower(). However, this presents an issue in the dynamic languages because they perform various failable checks in lower(): type checks, bounds checks, etc. If the check fails, then the cloned handle will be leaked. Prevent this by adding a `check()` method. We do the check first then once we get to `lower()`, nothing can fail.
Currently, `lower()` always returns a borrow of the object handle. This is fine for function arguments, since you know the object is still alive on the stack while the function is being called. However, for function returns this is not correct. To fix this: clone the handle in `lower()`. Added a test for this -- it was surprisingly easy to cause a segfault with the current behavior.
Currently, `lower()` always returns a borrow of the object handle. This is fine for function arguments, since you know the object is still alive on the stack while the function is being called. However, for function returns this is not correct. To fix this: clone the handle in `lower()`. Added a test for this -- it was surprisingly easy to cause a segfault with the current behavior. Removed the reexport-scaffolding-macro fixture which broke and often requires changes when the FFI changes. I don't think it's giving us enough value at this point to justify continuing to update it.
I want to fix mozilla#1797 by cloning the object handle in lower(). However, this presents an issue in the dynamic languages because they perform various failable checks in lower(): type checks, bounds checks, etc. If the check fails, then the cloned handle will be leaked. Prevent this by adding a `check()` method. We do the check first then once we get to `lower()`, nothing can fail.
I want to fix #1797 by cloning the object handle in lower(). However, this presents an issue in the dynamic languages because they perform various failable checks in lower(): type checks, bounds checks, etc. If the check fails, then the cloned handle will be leaked. Prevent this by adding a `check()` method. We do the check first then once we get to `lower()`, nothing can fail.
Currently, `lower()` always returns a borrow of the object handle. This is fine for function arguments, since you know the object is still alive on the stack while the function is being called. However, for function returns this is not correct. To fix this: clone the handle in `lower()`. Added a test for this -- it was surprisingly easy to cause a segfault with the current behavior. Removed the reexport-scaffolding-macro fixture which broke and often requires changes when the FFI changes. I don't think it's giving us enough value at this point to justify continuing to update it.
Currently, `lower()` always returns a borrow of the object handle. This is fine for function arguments, since you know the object is still alive on the stack while the function is being called. However, for function returns this is not correct. To fix this: clone the handle in `lower()`. Added a test for this -- it was surprisingly easy to cause a segfault with the current behavior. Removed the reexport-scaffolding-macro fixture which broke and often requires changes when the FFI changes. I don't think it's giving us enough value at this point to justify continuing to update it.
When an object is lowered from a foreign language we return the raw pointer from the foreign code then clone the Arc using the raw pointer in the Rust code.
How do we know that the pointer is still valid in that Rust code? For normal Rust calls, we can be sure since the a reference to the foreign object is still on the stack. However, for callbacks there are cases when this isn't true:
from_raw
on the freed raw pointer.I also think there's a related case when Rust returns a callback interface or trait interface.
The text was updated successfully, but these errors were encountered: