Skip to content

On the size_t / uintptr_t guard #329

Closed
@geofft

Description

@geofft

In #196 it was found that the __builtin_types_compatible_p(size_t, uintptr_t) check fails onCONFIG_ARM, and that PR skipped the check.

It turns out that the types are in fact compatible at the ABI level - they're both 32-bit integers. It turns out that they're just not compatible as C types. You can get a better error message if you try to assign a size_t pointer to a uintptr_t * or something:

/root/foo.c: In function 'init_module':
/root/foo.c:6:13: error: initialization of 'size_t *' {aka 'unsigned int *'} from incompatible pointer type 'uintptr_t *' {aka 'long unsigned int *'} [-Werror=incompatible-pointer-types]
 size_t *b = &a;

C doesn't believe that unsigned int and long unsigned int are compatible types in terms of the abstract C idea of int and long - but on 32-bit ARM, they're not actually different (what else could they be?).

So I think it should be safe to change this test to something else, like sizeof(size_t) == sizeof(uintptr_t) && __alignof__(size_t) == __alignof__(uintptr_t), which does pass on ARM, and make it not-platform-specific.


I added this check (without thinking too hard about how _builtin_types_compatible_p works) back in fishinabarrel/linux-kernel-module-rust@1e8ca1dc because, at least in the way we're using it, the way that rust-lang/rust-bindgen#1671 was fixed effectively adds a new bug. The bug it adds is that it translates the C size_t type to u32 or whatever instead of to usize, which means you can't straightforwardly use it in contexts where Rust expects a usize, e.g., slice indexing.

We are working around that (new) bug by passing --size_t-is-usize to bindgen (in rust/Makefile). But that simply assumes that the two types are compatible. I think in every practical place they are (and in particular, I'm pretty sure they are everywhere mainline Linux and/or Rust currently runs), but the bug originally reported in 1671 is that this isn't guaranteed by the C standard, and there might be places (like 16-bit x86 with segmentation, I think) where it's not.

In case we ever end up on such a platform, somehow, we don't want to be binding C functions that use size_t with a Rust usize because that's either ABI-incompatible or prone to integer overflow bugs. We either want to decide to not support that platform or to very carefully fix our uses of usize. (Or, possibly, try to get the kernel's ABI on that platform to change to make those types the same.)

That is, the fact that issue 1671 is closed doesn't mean we no longer need the guard. We very much need the guard on all platforms because we use --size_t-is-usize.


I'll send in a patch to fix the test and also improve the comment / assertion message.

I'm going to see if I can get a change into bindgen where bindgen itself does the compatibility check when it generates bindings and also defaults to --size_t-is-usize, and if the compatibility check fails (and something uses size_t), you need to specify an option saying that you're okay with binding size_t to u32/u64/whatever. If that lands, then we can remove the guard.

Metadata

Metadata

Assignees

No one assigned

    Labels

    • archRelated to a particular arch, `arch/` support in general...

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions