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

ServoBindingList.h (Servo_*) binding generation isn't type safe #12992

Closed
Manishearth opened this issue Aug 23, 2016 · 12 comments
Closed

ServoBindingList.h (Servo_*) binding generation isn't type safe #12992

Manishearth opened this issue Aug 23, 2016 · 12 comments

Comments

@Manishearth
Copy link
Member

@Manishearth Manishearth commented Aug 23, 2016

When bindgen is run on ServoBindingList.h, it spits out an extern C block full of Servo_* function declarations in bindings.rs

These functions are defined in glue.rs.

There's nothing stopping the declarations from having different signatures from the definitions. Which means that any safety measures we introduce are easy to accidentally circumvent by picking the wrong type.

We should either have a tidy check for this, or have bindgen generate a test file which imports everything from glue and does a type assertion on the function types.

cc @emilio @bholley

@bholley
Copy link
Contributor

@bholley bholley commented Aug 23, 2016

Yeah, we get a little bit of safety right now because LLVM crashes if the signatures differ too much (or at least, it did last I checked, which was probably March). :-P But I agree something systematic would be better. Open to doing it however you think is best.

@Manishearth
Copy link
Member Author

@Manishearth Manishearth commented Aug 24, 2016

Not really sure how to add it to bindgen without adding servo-specific code. We could make it generate tests for functions that share a prefix I guess. Or perhaps tell it to special-case functions found in that specific header file (maybe generate bindings_servo.rs?). We would then need to tell it where to find the functions to compare it with, though a glob import works. Still, it would be highly specialized code.

@bholley
Copy link
Contributor

@bholley bholley commented Aug 24, 2016

Can we just fix the rust compiler to complain if there are conflicting definitions of an extern "C" function in the same crate?

@bholley
Copy link
Contributor

@bholley bholley commented Aug 24, 2016

(Though I guess the bindings are a different crate - but we could use * them somewhere)

@Manishearth
Copy link
Member Author

@Manishearth Manishearth commented Aug 25, 2016

I .... don't know.

The "different crate" is a hurdle. Though like I said yesterday, almost all of geckolib (and all of gecko_bindings) could be rolled into style as a single crate.

Its also that we probably aren't using extern decls for their purpose here. You use them to declare functions you plan on linking to, so that you can call them from servo. You don't use them as a "header file"-esque thing to pre-declare functions you plan on defining later. We do this for Servo_* and it's totally unnecessary; it's just that bindgen does it. So I'm not sure if rust would add such a check even in the single-crate world.

@bholley
Copy link
Contributor

@bholley bholley commented Aug 30, 2016

Sounds like we're rolling up the crates, so that hurdle is gone.

Even if it's not the intended purpose, it seems reasonable that the rust compiler should check for two conflicting definitions of the same external function in the same crate (and I believe it does so implicitly right now, via the LLVM assert). We should verify that this assert still triggers, and get the rust folks to turn that into a more-useful compiler error.

Also, can you file this in bugzilla as a blocking bug against |stylo| so we don't lose track of it?

@Manishearth
Copy link
Member Author

@Manishearth Manishearth commented Oct 6, 2016

#13617 gets us the script, and https://bugzilla.mozilla.org/show_bug.cgi?id=1308234 fixes the type issues it found.

However, it doesn't work just yet. The problem is that glue.rs has safe extern C functions, and the functions in geckolib are extern C declarations (which are unsafe even if not explicitly specified).

I made all the Servo_ functions unsafe to find all the mismatched signatures fixed in bug 1308234, but that isn't a permanent solution. The whole point of the FFI safety stuff was to reduce the unsafe surface area; if glue.rs is unsafe-everywhere-by-default we lose out on this.

Sadly casting doesn't work either, Rust doesn't let you cast between fn pointer types. Ideally fn should be a subtype of unsafe fn, but that's not the case either.

This means we need a second version of glue.rs that has all the functions marked as unsafe. This is ... tricky. I tried using a macro with conditional compilation but there are some hurdles in parsing whole functions and putting them back together -- the follow rules don't allow the matchers to work.

The current solution I'm thinking of is to add a build script to tests/unit/stylo which takes glue.rs and generates a second version of the file with all the functions as unsafe, and uses that instead.

@Manishearth
Copy link
Member Author

@Manishearth Manishearth commented Oct 6, 2016

Interestingly, bindgen picks up the definition of Servo_Node_ClearNodeData from nsINode.h (where it is forward-declared) instead of ServoBindingList.h. @emilio, is this a bug?

@emilio
Copy link
Member

@emilio emilio commented Oct 6, 2016

It's not, is the same function with the same signature. We only generate the first function we see.

@Manishearth
Copy link
Member Author

@Manishearth Manishearth commented Oct 6, 2016

The signatures diverge after https://bugzilla.mozilla.org/show_bug.cgi?id=1308234 . Making them the same requires me to forward-declare the borrowed type too, which I can do (I instead moved it to the cpp file)

Ok, if that's expected behavior that's fine.

@emilio
Copy link
Member

@emilio emilio commented Oct 6, 2016

But they differ just in the typedefs right?

@Manishearth
Copy link
Member Author

@Manishearth Manishearth commented Oct 6, 2016

Yes. But we ask bindgen to generate bindigns for ServoBindingList.h, and it picks up the signature from the other file. The typedef matters because we rewrite types.

Manishearth added a commit to Manishearth/servo that referenced this issue Oct 6, 2016
Manishearth added a commit to Manishearth/servo that referenced this issue Oct 7, 2016
Manishearth added a commit to Manishearth/servo that referenced this issue Oct 9, 2016
bors-servo added a commit that referenced this issue Oct 9, 2016
Test that Servo_* functions have the right signatures

Fixes #12992

Needs #13617

Not very happy with this solution (and perhaps it should be done in pure Rust, though that can be split out as another easy bug).

But it works. The bindings changes are from running a regen on [bug 1308234](https://bugzilla.mozilla.org/show_bug.cgi?id=1308234)

r? @emilio

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/13620)
<!-- Reviewable:end -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
3 participants
You can’t perform that action at this time.