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

Tracking issue for `safe_extern_statics` compatibility lint #36247

Open
petrochenkov opened this Issue Sep 3, 2016 · 14 comments

Comments

Projects
None yet
8 participants
@petrochenkov
Contributor

petrochenkov commented Sep 3, 2016

What is this lint about

Consider the following code:

extern {
    static S: T;
}

S here is a static variable accessible to Rust but defined and initialized elsewhere.
External static variables cannot be accessed safely from Rust code for several reasons:

  • S can contain an arbitrary bit pattern not necessarily being a valid value of type T.
  • even if all bit patterns are valid values of T, S is still not controlled by Rust ownership system and can be accessed from other threads potentially creating data races.

See #35112 for more details as well as examples of how this can lead to crashes.

Previously safe accesses to extern statics were erroneously permitted outside of unsafe blocks. #36173 fixed this oversight.

How to fix this warning/error

Surround accesses to extern statics with unsafe blocks and make sure these accesses are actually valid.

Current status

  • #36173 introduces the safe_extern_statics lint as warn-by-default
  • #42894 makes the safe_extern_statics lint deny-by-default
  • PR ? makes the safe_extern_statics lint a hard error

bors added a commit that referenced this issue Sep 8, 2016

Auto merge of #36173 - petrochenkov:unstat, r=nikomatsakis
Issue deprecation warnings for safe accesses to extern statics

Fixes #35112
cc #36247

bors added a commit that referenced this issue Sep 8, 2016

Auto merge of #36173 - petrochenkov:unstat, r=nikomatsakis
Issue deprecation warnings for safe accesses to extern statics

Fixes #35112
cc #36247
@cbiffle

This comment has been minimized.

cbiffle commented Sep 19, 2016

In the case where I'm using an extern struct to model memory-mapped peripheral registers, and can design the API carefully to be safe, is there any way to disable this warning on a per-item basis? It seems that even naming the extern triggers the warning, so I cannot carefully implement methods on it to do safe things.

Example:

// GPIOD is an extern struct so that I can place it on memory-mapped
// registers and keep Rust from trying to initialize it.  Its only public
// operations are set_pin and clear_pin, both of which use unsafe code
// but are intended as safe APIs.  But safe code cannot do this:
stm32f4::gpio::GPIOD.set_pin(12);

One option I've considered is to always allocate two statics, one extern, and one a simple proxy containing unsafe-marked calls to its methods. I don't like this approach, but it will unstick me for now.

@petrochenkov

This comment has been minimized.

Contributor

petrochenkov commented Sep 19, 2016

@cbiffle
I think the proper solution is ability to mark unsafe-by-default items as safe, similarly to how safe-by-default pure Rust functions can be marked as unsafe.
In addition to foreign statics this can be applied to foreign functions and union fields.

@cbiffle

This comment has been minimized.

cbiffle commented Sep 19, 2016

@petrochenkov, yes, that is basically what I'm hoping for. Just as I can use unsafe {} to tell the compiler "I know this is composed of unsafe operations, but the result is safe, really," I'd like a way of indicating something similar at an item or type level.

Does it already exist? (I'm trying to get code working today.)

@petrochenkov

This comment has been minimized.

Contributor

petrochenkov commented Sep 19, 2016

Does it already exist? (I'm trying to get code working today.)

No. Even if someone submits an RFC right now, it'll be months until it's accepted (if it's accepted), implemented and hits stable.
The best advice I can give is to write a convenience macro for the proxy workaround you are using now.

A worse advice is to use #![allow(safe_extern_statics)].
Given that safe access to foreign statics was permitted for 1.5 years since 1.0, it won't probably become an error in the next 1-2 years and until some replacement is available.

Ms2ger added a commit to servo/rust-mozjs that referenced this issue Sep 22, 2016

Wrap uses of extern statics in unsafe blocks.
This fixes safe_extern_statics warnings; see
<rust-lang/rust#36247>.

bors-servo added a commit to servo/rust-mozjs that referenced this issue Sep 22, 2016

Auto merge of #307 - servo:36247, r=jdm
Wrap uses of extern statics in unsafe blocks.

This fixes safe_extern_statics warnings; see
<rust-lang/rust#36247>.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/rust-mozjs/307)
<!-- Reviewable:end -->

SSheldon added a commit to SSheldon/rust-dispatch that referenced this issue Jan 8, 2017

@nikomatsakis

This comment has been minimized.

Contributor

nikomatsakis commented Feb 17, 2017

cc @rust-lang/lang

So I had a long conversation with @wycats about this. He convinced me that simply declaring all accesses to statics as unsafe is not an optimal solution. Specifically, it is common in C code to have statics that are constant (never mutated), and right now there is no way to "wrap" those statics with a safe interface.

To see what I mean, consider wrapping a C function fn noop() that just does nothing. Clearly, this function is always safe. But of course rust doesn't know what it does, so we make it unsafe to call. But as the wrapper of this library, I can write a Rust function that is entirely equivalent:

mod sys {
    extern { fn noop(); }
}

fn noop() {
    // I assert that this is safe to call at any time
    unsafe { noop(); }
}

This is annoying, but mostly works. On the other hand, if I have a static constant, I am stuck. An example is that Ruby has a constant for a nil value, called Q_nil or something like that. The only wrapper I can write is not a constant, but rather a function that returns the value of the constant (or an &'static pointer to it). This seems distinctly more painful to use, which works against the ergonomic wrapping of C libraries.

I see two possible solutions here:

Define some way for FFI declarations in general to declare that the value is, in fact, safe. this could also be used to wrap noop. I'm not sure what this syntax is. Usually we use an unsafe block for this, but here we have no "code". It's too bad we never adopted the trust keyword, perhaps, but oh well.

Permit const in FFI blocks. This would be a way to assert that the static is indeed a constant (e.g., extern { const Q_nil: u32; }. This seems like a nice thing to make easy. My one hesitation is that constants, in Rust, have some kind of funny-ish semantics, e.g., they don't have a defined address, that is probably not what we want here. We might also write extern { static const Q_nil: u32 }, but that is kind of confusing too.

Regardles, we should probably handle this case more nicely I think!

@nikomatsakis

This comment has been minimized.

Contributor

nikomatsakis commented Feb 17, 2017

Er, I'm actually just repeating what @cbiffle wrote, I suppose. =) Sorry, missed those comments.

@petrochenkov

This comment has been minimized.

Contributor

petrochenkov commented Feb 17, 2017

@nikomatsakis
You'll be surprised, I submitted RFC 1901 "Reverting default unsafety" inspired by @cbiffle's comments just two days ago.
(People don't seem to like it so far 😄 )

@eddyb

This comment has been minimized.

Member

eddyb commented Feb 17, 2017

Are we disallowing const FOO: &'static Foo = unsafe { &C_FOO }; right now?
We could allow it and then disallow any operations which may cause a read (which I think we do correctly right now?) and also disallow passing it to const fn (which I believe was the big issue with it).
Or we could "just" evaluate these with miri and allow reads of statics, which would work fine for Rust ones (unless there's a cycle which on-demand would catch), while FFI ones would error on read/write.
cc @solson

@nikomatsakis

This comment has been minimized.

Contributor

nikomatsakis commented Feb 17, 2017

@petrochenkov I am indeed =) I'm always like 2 steps behind on the RFC repo...working on catching up though!

@nikomatsakis

This comment has been minimized.

Contributor

nikomatsakis commented Feb 17, 2017

@eddyb indeed that's a nifty thought, although it means users will have to write *FOO

@joshtriplett

This comment has been minimized.

Member

joshtriplett commented Feb 18, 2017

@nikomatsakis While const in Rust does allow Rust to omit any corresponding runtime storage or symbol, we could define extern const (or for that matter extern "C" const) as always referencing a symbol, FFI-style.

@retep998

This comment has been minimized.

Member

retep998 commented Feb 18, 2017

Const has some additional semantics over static, such as the ability to use it in patterns. Would extern consts also gain these semantics? (I'm opposed either way)

@petrochenkov petrochenkov added the A-lint label Feb 19, 2017

@nikomatsakis

This comment has been minimized.

Contributor

nikomatsakis commented Feb 21, 2017

@retep998

I think extern const is the wrong syntax, since this is not a "Rust const", but rather a static variable that the FFI author is promising will not be re-assigned. I do feel this is a very useful thing to be able to express, but i'm not sure what's the best way to do it.

ids1024 added a commit to ids1024/ralloc that referenced this issue Nov 8, 2017

Use new allocator API, and otherwise update for new Rust
- New allocator API
- Remove the "allocator" feature, which should be unnecessary due to how
the new allocator API works
- NonZero no longer implements Deref (rust-lang/rust#41064)
- NonZero::new() returns an Option; use NonZero::new_unchecked()
- Thread locals are no longer 'static (rust-lang/rust#43746)
- Changes to feature flags
- Use unsafe to access extern static (rust-lang/rust#36247)

jdm added a commit to servo/mozjs that referenced this issue Dec 18, 2017

Wrap uses of extern statics in unsafe blocks.
This fixes safe_extern_statics warnings; see
<rust-lang/rust#36247>.
@Centril

This comment has been minimized.

Contributor

Centril commented Nov 11, 2018

@joshtriplett what's to be done here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment