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

Prevent unwinding past FFI boundaries #46833

Merged
merged 3 commits into from Dec 24, 2017

Conversation

@diwic
Copy link
Contributor

commented Dec 19, 2017

Second attempt to write a patch to solve this.

r? @nikomatsakis

So, my biggest issue with this patch is the way the patch determines what functions should have an abort landing pad (in construct_fn). I would ideally have this code match src/librustc_trans/callee.rs::get_fn but couldn't find an id that returns true for is_foreign_item. Also tried tcx.has_attr("unwind") with no luck. FIXED

Other issues:

  • llvm.trap is an SIGILL on amd64. Ideally we could use panic-abort's version of aborting which is nicer but we don't want to depend on that library...

  • Mir inlining is a stub currently. FIXED (no-op)

Also, when reviewing please take into account that I'm new to the code and only partially know what I'm doing... and that I've mostly made made matches on TerminatorKind::Abort match either TerminatorKind::Resume or TerminatorKind::Unreachable based on what looked best.

@rust-highfive

This comment has been minimized.

Copy link
Collaborator

commented Dec 19, 2017

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @nikomatsakis (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

Please see the contribution instructions for more information.

@bors

This comment has been minimized.

Copy link
Contributor

commented Dec 19, 2017

☔️ The latest upstream changes (presumably #45525) made this pull request unmergeable. Please resolve the merge conflicts.

@@ -806,6 +806,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> {
*kind = TerminatorKind::Goto { target: tgt }
}
}
TerminatorKind::Abort => { unimplemented!("Not sure what to do here?!"); }

This comment has been minimized.

Copy link
@arielb1

arielb1 Dec 19, 2017

Contributor

This should be a no-op - there are no targets to update.

This comment has been minimized.

Copy link
@arielb1

arielb1 Dec 19, 2017

Contributor

aka the same as TerminatorKind::Unreachable

This comment has been minimized.

Copy link
@diwic

diwic Dec 19, 2017

Author Contributor

Thanks! Will fix for next version.


// FIXME: Figure out why we can't use something like this instead:
// tcx.is_foreign_item(tcx.hir.local_def_id(fn_id));
// tcx.has_attr(tcx.hir.local_def_id(fn_id), "unwind");

This comment has been minimized.

Copy link
@arielb1

arielb1 Dec 19, 2017

Contributor

What happens if you use has_attr? Why doesn't it work?

This comment has been minimized.

Copy link
@diwic

diwic Dec 19, 2017

Author Contributor

AFAICT, tcx.has_attr(tcx.hir.local_def_id(fn_id), "unwind") returns false also for "__rust_start_panic" and "panicking::rust_begin_panic".

I don't know why, all these different contexts and ids are a bit bewildering to me. My guess is that I'm trying with the wrong ID or something, but then I don't know what ID would be the right one.

This comment has been minimized.

Copy link
@diwic

diwic Dec 21, 2017

Author Contributor

Scratch this comment, I must have done something wrong. It does seem to work in the new - just pushed - version.

// Therefore generate an extra "Abort" landing pad.

// FIXME: Figure out why we can't use something like this instead:
// tcx.is_foreign_item(tcx.hir.local_def_id(fn_id));

This comment has been minimized.

Copy link
@arielb1

arielb1 Dec 19, 2017

Contributor

is_foreign_item checks for foreign items, aka

extern "C" {
    fn foreign_item(); // we don't generate MIR for this
}

Rather than extern fns, aka

extern "C" fn extern_fn() {
    // we *do* generate MIR for this
}
@arielb1

This comment has been minimized.

Copy link
Contributor

commented Dec 19, 2017

[00:04:12] tidy error: /checkout/src/librustc_mir/build/scope.rs:619: line longer than 100 chars

pub fn schedule_abort(&mut self) -> BasicBlock {
self.scopes[0].needs_cleanup = true;
let abortblk = self.cfg.start_new_cleanup_block();
self.cfg.terminate(abortblk, self.scopes[0].source_info(self.fn_span), TerminatorKind::Abort);

This comment has been minimized.

Copy link
@arielb1

arielb1 Dec 19, 2017

Contributor

Line longer than 100 chars

This comment has been minimized.

Copy link
@diwic

diwic Dec 19, 2017

Author Contributor

Thanks, will fix for next version.

// tcx.is_foreign_item(tcx.hir.local_def_id(fn_id));
// tcx.has_attr(tcx.hir.local_def_id(fn_id), "unwind");

let is_foreign = match abi {

This comment has been minimized.

Copy link
@nikomatsakis

nikomatsakis Dec 19, 2017

Contributor

Actually, I think that an abi check is exactly the right thing. The key point is that the "C" ABI (and other non-Rust ABIs) don't have a defined way to propagate Rust panics, right?

This comment has been minimized.

Copy link
@diwic

diwic Dec 19, 2017

Author Contributor

I'm not so sure.

"__rust_start_panic" and "panicking::rust_begin_panic" are of C ABI and are able to panic, so it can't be that undefined...

Rather, the real danger is when we tell LLVM that a function is "nounwind" and then we end up panicking within - or through - it. That's the undefined behavior this patch is trying to resolve.
So, then I was trying to figure out when we actually mark a function as "nounwind", and it seems now I did not look closely enough. The algorithm seems to be:

  1. ABI check - so you're right, it should be an ABI check.
  2. Set as unwinding if there is an unwind attribute
  3. Set as unwinding if it isn't a foreign item

So maybe that's what I'm supposed to mimic, or possibly try to refactor somehow if we need it in two places?

This comment has been minimized.

Copy link
@nikomatsakis

nikomatsakis Dec 19, 2017

Contributor

It seems like it would be ideal to have the criteria extracted into a helper function, yes.

This comment has been minimized.

Copy link
@arielb1

arielb1 Dec 19, 2017

Contributor

I'm not sure extracting the criteria directly would help:

  1. Unwinding across a lang boundary is "instant" LLVM UB as we emit the nounwind attribute. If we want to continue doing that, we can't also make it trap.
  2. Therefore, we want to catch unwinding before we reach the lang boundary. That means stopping unwinding from proceeding from Rust to C, because we don't control the C-to-Rust lang boundary.
  3. This means that we need to prevent unwinding on non-foreign items, which means we need to ignore the code for (3) from the previous list.

Disabling the check that allows non-foreign C ABI Rust functions to unwind would allow us to make these functions abort on unwind.

This comment has been minimized.

Copy link
@nikomatsakis

nikomatsakis Dec 20, 2017

Contributor

I must be confused about something. @arielb1 what do you mean by this:

Unwinding across a lang boundary is "instant" LLVM UB as we emit the nounwind attribute. If we want to continue doing that, we can't also make it trap.

Do you mean that the call is tagged with nounwind, or the function? I was assuming the latter.

This comment has been minimized.

Copy link
@arielb1

arielb1 Dec 20, 2017

Contributor

It doesn't matter whether the call or the function are tagged as nounwind. In both cases, unwinding is UB LLVM-side and therefore can't be turned to an abort..

This comment has been minimized.

Copy link
@nikomatsakis

nikomatsakis Dec 20, 2017

Contributor

I thnk we are talking past each other to a certain extent. Let me first define a set of functions I will call "border functions" -- i.e., functions implemented in Rust but which are invokable from C and hence have C ABI. For these functions, it is considered UB if they unwind (and hence these border functions may also be marked as "no-unwind"). In that case, when we generate the fn body, we can trap/abort if an unwind does occur. (This is, I believe, the same thing C++ does in such cases, though I may be mistaken.) This costs us nothing to the same extent that unwinding is "zero cost".

I guess you are saying that we should ignore the #[unwind] attribute for the purpose of this trap, and generate it anyway? This is (I guess) because C code may still call such a function? That sort of makes sense, though it does raise the question of the purpose of the #[unwind] attribute.

This comment has been minimized.

Copy link
@arielb1

arielb1 Dec 20, 2017

Contributor

Sure enough. So for that we'll have to remove the "Set as unwinding if it isn't a foreign item" check.

@nikomatsakis

This comment has been minimized.

Copy link
Contributor

commented Dec 20, 2017

OK, so @arielb1 and I were chatting on gitter, and we came to roughly this conclusion:

  • We can emit the trap on functions with non-Rust ABI.
    • As @arielb1 said above, the "foreign item" check is not relevant here.
  • We should permit such functions to have a #[unwind] attribute, which would suppress the trap.

Note though that this is a change in behavior -- albeit only quasi-defined behavior -- and it feels like it ought to go through the RFC process. Still, it'd be good to have a working implementation so that we can do a crater run and assess possible impact.

So it may not be that there is a "common helper" to extract, that's not entirely clear to me.

diwic added some commits Dec 19, 2017

Mir: Add Terminatorkind::Abort
The Abort Terminatorkind will cause an llvm.trap function call to be
emitted.

Signed-off-by: David Henningsson <diwic@ubuntu.com>
Mir: Abort on nounwind ABIs
Generate Abort instead of Resume terminators on nounwind ABIs.

#18510

Signed-off-by: David Henningsson <diwic@ubuntu.com>

@diwic diwic force-pushed the diwic:7c-abort-ffi branch from 5935057 to f536143 Dec 21, 2017

@diwic

This comment has been minimized.

Copy link
Contributor Author

commented Dec 21, 2017

We can emit the trap on functions with non-Rust ABI.
As @arielb1 said above, the "foreign item" check is not relevant here.
We should permit such functions to have a #[unwind] attribute, which would suppress the trap.

Ok, so I think we're mostly on the same page w r t what needs to be done. I rebased it on top of master and skipped the "common helper" part.

Note though that this is a change in behavior -- albeit only quasi-defined behavior -- and it feels like it ought to go through the RFC process. Still, it'd be good to have a working implementation so that we can do a crater run and assess possible impact.

Hmm, so I was thinking "what could this possibly break" and came up with this contrived example:

extern "C" fn foo(called_from_rust: bool) { 
    if something_really_bad_happens() {
        if called_from_rust { panic!("Oh no"); }
        else { std::process::abort() }
    }
}

But even in this case; looking at the LLVM IR, we mark this function as nounwind today (not sure why - the "foreign item" check should be false so unwind should have been added?) - so even if this code seems to work in practice, it's UB in theory because we're unwinding from a nounwind function.

EDIT: So what I wanted to say - is this ever a change in behavior where the previous behavior was not UB?

@diwic

This comment has been minimized.

Copy link
Contributor Author

commented Dec 21, 2017

@kennytm Is there a way I can remove the "waiting on author" tag, now that I've responded and so it is no longer waiting for me (but for CI and a new review pass)?

@diwic

This comment has been minimized.

Copy link
Contributor Author

commented Dec 21, 2017

Btw: Not sure about the current status of #[unwind] - should it be an unsafe attribute or not? Should it be behind a feature gate?

Also, rust_begin_unwind was relabelled to rust_begin_panic a long time ago, still it shows up as rust_begin_unwind in backtraces. This is a bit confusing. I could change that in a separate PR.

@kennytm

This comment has been minimized.

Copy link
Member

commented Dec 21, 2017

@diwic Retagged :)

If this PR is no longer a work-in-progress, please also remove the "WIP" from the title.

@arielb1

This comment has been minimized.

Copy link
Contributor

commented Dec 21, 2017

@diwic

Why should #[unwind] be an unsafe attribute? It makes extern "C" functions panic rather than abort, which could have unexpected effects if they are used for FFI, but FFI is unsafe anyway.

@@ -383,6 +405,11 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
let source_info = builder.source_info(span);
let call_site_s = (call_site_scope, source_info);
unpack!(block = builder.in_scope(call_site_s, LintLevel::Inherited, block, |builder| {

This comment has been minimized.

Copy link
@arielb1

arielb1 Dec 21, 2017

Contributor

stray newline

@@ -353,6 +354,27 @@ macro_rules! unpack {
};
}

fn needs_abort_block<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,

This comment has been minimized.

Copy link
@arielb1

arielb1 Dec 21, 2017

Contributor

Could you rename this to should_abort_on_panic instead?

@arielb1

This comment has been minimized.

Copy link
Contributor

commented Dec 21, 2017

r=me with nits addressed

@arielb1 arielb1 changed the title [WIP] prevent unwinding past FFI boundaries Prevent unwinding past FFI boundaries Dec 21, 2017

Mir: fixup nits in previous commit (f536143)
As suggested by arielb1.

Closes #18510

Signed-off-by: David Henningsson <diwic@ubuntu.com>
@diwic

This comment has been minimized.

Copy link
Contributor Author

commented Dec 21, 2017

@arielb1

Why should #[unwind] be an unsafe attribute? It makes extern "C" functions panic rather than abort, which could have unexpected effects if they are used for FFI, but FFI is unsafe anyway.

Good point. FFI can call Rust functions too with the wrong calling convention, this is no worse really.

Nits addressed.

@arielb1

This comment has been minimized.

Copy link
Contributor

commented Dec 23, 2017

@bors r+

@bors

This comment has been minimized.

Copy link
Contributor

commented Dec 23, 2017

📌 Commit 4910ed2 has been approved by arielb1

@nagisa nagisa referenced this pull request Feb 9, 2018

nikomatsakis added a commit to nikomatsakis/rust that referenced this pull request Feb 20, 2018

nikomatsakis added a commit to nikomatsakis/rust that referenced this pull request Feb 20, 2018

nikomatsakis added a commit to nikomatsakis/rust that referenced this pull request Feb 20, 2018

nikomatsakis added a commit to nikomatsakis/rust that referenced this pull request Feb 20, 2018

nikomatsakis added a commit to nikomatsakis/rust that referenced this pull request Feb 22, 2018

Mark-Simulacrum added a commit to Mark-Simulacrum/rust that referenced this pull request Feb 22, 2018

bors added a commit that referenced this pull request Feb 23, 2018

bors added a commit that referenced this pull request Feb 24, 2018

bors added a commit that referenced this pull request Feb 25, 2018

bors added a commit that referenced this pull request Feb 25, 2018

bors added a commit that referenced this pull request Feb 25, 2018

bors added a commit that referenced this pull request Feb 26, 2018

bors added a commit that referenced this pull request Feb 26, 2018

alexcrichton added a commit to alexcrichton/rust that referenced this pull request Feb 27, 2018

rustc: Tweak funclet cleanups of ffi functions
This commit is targeted at addressing rust-lang#48251 by specifically fixing a case where
a longjmp over Rust frames on MSVC runs cleanups, accidentally running the
"abort the program" cleanup as well. Added in rust-lang#46833 `extern` ABI functions in
Rust will abort the process if Rust panics, and currently this is modeled as a
normal cleanup like all other destructors.

Unfortunately it turns out that `longjmp` on MSVC is implemented with SEH, the
same mechanism used to implement panics in Rust. This means that `longjmp` over
Rust frames will run Rust cleanups (even though we don't necessarily want it
to). Notably this means that if you `longjmp` over a Rust stack frame then that
probably means you'll abort the program because one of the cleanups will abort
the process.

After some discussion on IRC it turns out that `longjmp` doesn't run cleanups
for *caught* exceptions, it only runs cleanups for cleanup pads. Using this
information this commit tweaks the codegen for an `extern` function to
a catch-all clause for exceptions instead of a cleanup block. This catch-all is
equivalent to the C++ code:

    try {
        foo();
    } catch (...) {
        bar();
    }

and in fact our codegen here is designed to match exactly what clang emits for
that C++ code!

With this tweak a longjmp over Rust code will no longer abort the process. A
longjmp will continue to "accidentally" run Rust cleanups (destructors) on MSVC.
Other non-MSVC platforms will not rust destructors with a longjmp, so we'll
probably still recommend "don't have destructors on the stack", but in any case
this is a more surgical fix than rust-lang#48567 and should help us stick to standard
personality functions a bit longer.

alexcrichton added a commit to alexcrichton/rust that referenced this pull request Feb 27, 2018

rustc: Tweak funclet cleanups of ffi functions
This commit is targeted at addressing rust-lang#48251 by specifically fixing a case where
a longjmp over Rust frames on MSVC runs cleanups, accidentally running the
"abort the program" cleanup as well. Added in rust-lang#46833 `extern` ABI functions in
Rust will abort the process if Rust panics, and currently this is modeled as a
normal cleanup like all other destructors.

Unfortunately it turns out that `longjmp` on MSVC is implemented with SEH, the
same mechanism used to implement panics in Rust. This means that `longjmp` over
Rust frames will run Rust cleanups (even though we don't necessarily want it
to). Notably this means that if you `longjmp` over a Rust stack frame then that
probably means you'll abort the program because one of the cleanups will abort
the process.

After some discussion on IRC it turns out that `longjmp` doesn't run cleanups
for *caught* exceptions, it only runs cleanups for cleanup pads. Using this
information this commit tweaks the codegen for an `extern` function to
a catch-all clause for exceptions instead of a cleanup block. This catch-all is
equivalent to the C++ code:

    try {
        foo();
    } catch (...) {
        bar();
    }

and in fact our codegen here is designed to match exactly what clang emits for
that C++ code!

With this tweak a longjmp over Rust code will no longer abort the process. A
longjmp will continue to "accidentally" run Rust cleanups (destructors) on MSVC.
Other non-MSVC platforms will not rust destructors with a longjmp, so we'll
probably still recommend "don't have destructors on the stack", but in any case
this is a more surgical fix than rust-lang#48567 and should help us stick to standard
personality functions a bit longer.

Manishearth added a commit to Manishearth/rust that referenced this pull request Feb 28, 2018

Rollup merge of rust-lang#48572 - alexcrichton:noexcept-msvc2, r=eddyb
rustc: Tweak funclet cleanups of ffi functions

This commit is targeted at addressing rust-lang#48251 by specifically fixing a case where
a longjmp over Rust frames on MSVC runs cleanups, accidentally running the
"abort the program" cleanup as well. Added in rust-lang#46833 `extern` ABI functions in
Rust will abort the process if Rust panics, and currently this is modeled as a
normal cleanup like all other destructors.

Unfortunately it turns out that `longjmp` on MSVC is implemented with SEH, the
same mechanism used to implement panics in Rust. This means that `longjmp` over
Rust frames will run Rust cleanups (even though we don't necessarily want it
to). Notably this means that if you `longjmp` over a Rust stack frame then that
probably means you'll abort the program because one of the cleanups will abort
the process.

After some discussion on IRC it turns out that `longjmp` doesn't run cleanups
for *caught* exceptions, it only runs cleanups for cleanup pads. Using this
information this commit tweaks the codegen for an `extern` function to
a catch-all clause for exceptions instead of a cleanup block. This catch-all is
equivalent to the C++ code:

    try {
        foo();
    } catch (...) {
        bar();
    }

and in fact our codegen here is designed to match exactly what clang emits for
that C++ code!

With this tweak a longjmp over Rust code will no longer abort the process. A
longjmp will continue to "accidentally" run Rust cleanups (destructors) on MSVC.
Other non-MSVC platforms will not rust destructors with a longjmp, so we'll
probably still recommend "don't have destructors on the stack", but in any case
this is a more surgical fix than rust-lang#48567 and should help us stick to standard
personality functions a bit longer.

alexcrichton added a commit to alexcrichton/rust that referenced this pull request Feb 28, 2018

rustc: Tweak funclet cleanups of ffi functions
This commit is targeted at addressing rust-lang#48251 by specifically fixing a case where
a longjmp over Rust frames on MSVC runs cleanups, accidentally running the
"abort the program" cleanup as well. Added in rust-lang#46833 `extern` ABI functions in
Rust will abort the process if Rust panics, and currently this is modeled as a
normal cleanup like all other destructors.

Unfortunately it turns out that `longjmp` on MSVC is implemented with SEH, the
same mechanism used to implement panics in Rust. This means that `longjmp` over
Rust frames will run Rust cleanups (even though we don't necessarily want it
to). Notably this means that if you `longjmp` over a Rust stack frame then that
probably means you'll abort the program because one of the cleanups will abort
the process.

After some discussion on IRC it turns out that `longjmp` doesn't run cleanups
for *caught* exceptions, it only runs cleanups for cleanup pads. Using this
information this commit tweaks the codegen for an `extern` function to
a catch-all clause for exceptions instead of a cleanup block. This catch-all is
equivalent to the C++ code:

    try {
        foo();
    } catch (...) {
        bar();
    }

and in fact our codegen here is designed to match exactly what clang emits for
that C++ code!

With this tweak a longjmp over Rust code will no longer abort the process. A
longjmp will continue to "accidentally" run Rust cleanups (destructors) on MSVC.
Other non-MSVC platforms will not rust destructors with a longjmp, so we'll
probably still recommend "don't have destructors on the stack", but in any case
this is a more surgical fix than rust-lang#48567 and should help us stick to standard
personality functions a bit longer.

Manishearth added a commit to Manishearth/rust that referenced this pull request Mar 1, 2018

Rollup merge of rust-lang#48572 - alexcrichton:noexcept-msvc2, r=eddyb
rustc: Tweak funclet cleanups of ffi functions

This commit is targeted at addressing rust-lang#48251 by specifically fixing a case where
a longjmp over Rust frames on MSVC runs cleanups, accidentally running the
"abort the program" cleanup as well. Added in rust-lang#46833 `extern` ABI functions in
Rust will abort the process if Rust panics, and currently this is modeled as a
normal cleanup like all other destructors.

Unfortunately it turns out that `longjmp` on MSVC is implemented with SEH, the
same mechanism used to implement panics in Rust. This means that `longjmp` over
Rust frames will run Rust cleanups (even though we don't necessarily want it
to). Notably this means that if you `longjmp` over a Rust stack frame then that
probably means you'll abort the program because one of the cleanups will abort
the process.

After some discussion on IRC it turns out that `longjmp` doesn't run cleanups
for *caught* exceptions, it only runs cleanups for cleanup pads. Using this
information this commit tweaks the codegen for an `extern` function to
a catch-all clause for exceptions instead of a cleanup block. This catch-all is
equivalent to the C++ code:

    try {
        foo();
    } catch (...) {
        bar();
    }

and in fact our codegen here is designed to match exactly what clang emits for
that C++ code!

With this tweak a longjmp over Rust code will no longer abort the process. A
longjmp will continue to "accidentally" run Rust cleanups (destructors) on MSVC.
Other non-MSVC platforms will not rust destructors with a longjmp, so we'll
probably still recommend "don't have destructors on the stack", but in any case
this is a more surgical fix than rust-lang#48567 and should help us stick to standard
personality functions a bit longer.

Manishearth added a commit to Manishearth/rust that referenced this pull request Mar 1, 2018

Rollup merge of rust-lang#48572 - alexcrichton:noexcept-msvc2, r=eddyb
rustc: Tweak funclet cleanups of ffi functions

This commit is targeted at addressing rust-lang#48251 by specifically fixing a case where
a longjmp over Rust frames on MSVC runs cleanups, accidentally running the
"abort the program" cleanup as well. Added in rust-lang#46833 `extern` ABI functions in
Rust will abort the process if Rust panics, and currently this is modeled as a
normal cleanup like all other destructors.

Unfortunately it turns out that `longjmp` on MSVC is implemented with SEH, the
same mechanism used to implement panics in Rust. This means that `longjmp` over
Rust frames will run Rust cleanups (even though we don't necessarily want it
to). Notably this means that if you `longjmp` over a Rust stack frame then that
probably means you'll abort the program because one of the cleanups will abort
the process.

After some discussion on IRC it turns out that `longjmp` doesn't run cleanups
for *caught* exceptions, it only runs cleanups for cleanup pads. Using this
information this commit tweaks the codegen for an `extern` function to
a catch-all clause for exceptions instead of a cleanup block. This catch-all is
equivalent to the C++ code:

    try {
        foo();
    } catch (...) {
        bar();
    }

and in fact our codegen here is designed to match exactly what clang emits for
that C++ code!

With this tweak a longjmp over Rust code will no longer abort the process. A
longjmp will continue to "accidentally" run Rust cleanups (destructors) on MSVC.
Other non-MSVC platforms will not rust destructors with a longjmp, so we'll
probably still recommend "don't have destructors on the stack", but in any case
this is a more surgical fix than rust-lang#48567 and should help us stick to standard
personality functions a bit longer.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
10 participants
You can’t perform that action at this time.