Closed alexlapa closed 2 months ago
Hi! Thanks for opening this pull request! :smile:
Great catch!
Btw, I wonder what platform are you using? Dart int
is 64bit (Web is 32bit; but the pointers are also 32bit on web), so in non-web platforms the conversion seems should not convert to u32::max.
EDIT: I see the problem: It is truncated to 2^63 - 1
. i.e. the raw pointer exceeds i64 (but within range of u64), and since Dart int
is indeed i64, it is saturated.
(If you need I can provide some suggestions for e.g. what precommit subcommand to run to make CI happy)
(If you need I can provide some suggestions for e.g. what precommit subcommand to run to make CI happy)
Yeah, i've seen that precommit script but it wasnt working for me until i've rolled back local rust and flutter versions.
And i would like to correct myself - BigInt.toInt()
is not a problem and it works as expected (0xffffffff.toInt() == -1
), its this toUnsigned(64)
that breaks pointers. So, i believe this fix can be reworked to continue using usize
for pointers, so let me know if you think this is the right way.
Another thing that bothers me is that there is a pretty high possibility of a similar issue happening in some other cases, since passing pointers as usize
is a pretty common thing, and all usize
s are treated with toUnsigned(64)
on the Dart side it seems. Maybe there is a same issue with the dco
codec?
Other than that i believe its ready for review.
Great job!
Yes, I agree it is the case when numbers is bigger than isize (still within usize range).
Another thing that bothers me is that there is a pretty high possibility of a similar issue happening in some other cases, since passing pointers as usize is a pretty common thing, and all usizes are treated with toUnsigned(64) on the Dart side it seems. Maybe there is a same issue with the dco codec?
That looks reasonable. To test cst & dco codec, you can (temporarily) set full_dep: true
in config.
Btw, is it possible we somehow add some tests for this scenario? Then we can ensure it never regress in the future by using CI, and we will also auto test the DCO codec etc.
Another btw: It seems https://api.dart.dev/stable/3.5.2/dart-ffi/Pointer/address.html, i.e. a pointer is of type int
(i64 on non-web). Thus curious what will happen when Dart sees an address like your case which is larger than maximum of i64...
Too busy and a bit tired now, thus will try to review this PR within a day :)
To test cst & dco codec, you can (temporarily) set full_dep: true in config.
So i've run some tests and returning DartOpaque
from Rust to Dart works fine using dco even without this PR, so its not affected.
Btw, is it possible we somehow add some tests for this scenario?
Meh, i dont think there is an easy way to do this.
Running test on CI on a arm64-v8a
Android will indirectly check for this issue, but thats not really a specific test. And that might be problematic since AFAIK there are some limitations of android emulator, i believe you cant run arm Android on x86 host since 27(?) API. Running that on a MacOS with Apple silicone might do the job but i never checked that.
Another option is implementing allocator(wrapping default one?) that will always return pointers that are bigger then i64::MAX
. But im not really sure how to do that correctly.
Another option is not using MirTypePrimitive::Isize
or MirTypePrimitive::Usize
for this but maybe adding MirTypePrimitive::Ptr
so we might add some tests for this type that will just check the hardcoded values. Like return u64::MAX
from Rust and assert that its -1
in Dart. This way it would also make *const ()
a correct return for the Rust exported functions, which would be pretty cool.
Thus curious what will happen when Dart sees an address like your case which is larger than maximum of i64
So web is out of the question since its 32-bit. And io will be fine since u64 can be represented as i64. E.g: Rust will return u64::MAX
, it will be translated to -1
in Dart, and translated back to the correct value when its back in Rust. As i previously mentioned it is toUnsigned(64).toInt()
that is the problem:
0xffffffffffffffff.toInt() = -1
0xffffffffffffffff.b.toUnsigned(64) = 18446744073709551615
0xffffffffffffffff.b.toUnsigned(64).toInt() = 9223372036854775807
So i've run some tests and returning DartOpaque from Rust to Dart works fine using dco even without this PR, so its not affected.
Great!
Meh, i dont think there is an easy way to do this.
I also think so... The environment is not trivial to create, and the unit tests may not cover wide enough.
it will be translated to -1 in Dart, and translated back to the correct value when its back in Rust
Yes, I guess it would be great for the doc to mention something about that (though surely optional)
(I will review the PR in detail probably tonight)
Btw, feel free to ping me (or use request a review
button) when ready!
Great! I will probably review within 24hours
Hi! Congrats on merging your first pull request! :tada:
Great job!
@all-contributors please add @alexlapa for code
@fzyzcjy
I've put up a pull request to add @alexlapa! :tada:
Changes
So, ive encountered a segfault on Android devices after upgrading to frb v2.2.
So whats going on is if a pointer does not fit in u32, it is being changed to u32::max right here which i was able to confirm with additional logging:
Another thing is that for some reason most targets tend to create pointer with values that fit into u32 so it works fine on macOs on Intel silicon, Linux and android x86_64 thats probably why this problem was able to stay under the radar.
Thats also possible that the same exact problem might happen in some other places, since passing pointers as usize is a pretty common thing i believe, but with this fix all my tests pass and app is working properly. However there might be some other scenarios that im not aware of (perhaps when using other codecs?).
Checklist
./frb_internal precommit --mode slow
(orfast
) is run (it internal runs code generator, does auto formatting, etc)../website
folder) are updated.Remark for PR creator
./frb_internal --help
shows utilities for development.