drivers/android/binder/Makefile | 3 +-- drivers/android/binder/page_range.rs | 6 +++--- drivers/android/binder/page_range_helper.c | 24 ------------------------ drivers/android/binder/page_range_helper.h | 15 --------------- rust/bindgen_parameters | 4 ++++ rust/bindings/bindings_helper.h | 1 - rust/bindings/lib.rs | 1 + rust/uapi/lib.rs | 1 + 8 files changed, 10 insertions(+), 45 deletions(-)
By default bindgen will convert 'enum lru_status' into a typedef for an
integer, but this leads to the wrong cfi type. It's supposed to be a
type called "lru_status" rather than the underlying native integer type.
To fix this, tell bindgen to generate a newtype and set the CFI type
explicitly. Note that we need to set the CFI attribute explicitly as
bindgen is using repr(transparent), which is otherwise identical to the
inner type for ABI purposes.
This allows us to remove the page range helper C function in Binder
without risking a CFI failure when list_lru_walk calls the provided
function pointer.
This requires bindgen v0.71 or greater.
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
drivers/android/binder/Makefile | 3 +--
drivers/android/binder/page_range.rs | 6 +++---
drivers/android/binder/page_range_helper.c | 24 ------------------------
drivers/android/binder/page_range_helper.h | 15 ---------------
rust/bindgen_parameters | 4 ++++
rust/bindings/bindings_helper.h | 1 -
rust/bindings/lib.rs | 1 +
rust/uapi/lib.rs | 1 +
8 files changed, 10 insertions(+), 45 deletions(-)
diff --git a/drivers/android/binder/Makefile b/drivers/android/binder/Makefile
index 09eabb527fa092b659559367705fd3667db6cb2c..7e0cd9782a8b24db598034e15e5a36eca91b3fa9 100644
--- a/drivers/android/binder/Makefile
+++ b/drivers/android/binder/Makefile
@@ -5,5 +5,4 @@ obj-$(CONFIG_ANDROID_BINDER_IPC_RUST) += rust_binder.o
rust_binder-y := \
rust_binder_main.o \
rust_binderfs.o \
- rust_binder_events.o \
- page_range_helper.o
+ rust_binder_events.o
diff --git a/drivers/android/binder/page_range.rs b/drivers/android/binder/page_range.rs
index 9379038f61f513c51ebed6c7e7b6fde32e5b8d06..eb738e169525839a199132dd71e69e0b9cc69053 100644
--- a/drivers/android/binder/page_range.rs
+++ b/drivers/android/binder/page_range.rs
@@ -642,15 +642,15 @@ fn drop(self: Pin<&mut Self>) {
unsafe {
bindings::list_lru_walk(
list_lru,
- Some(bindings::rust_shrink_free_page_wrap),
+ Some(rust_shrink_free_page),
ptr::null_mut(),
nr_to_scan,
)
}
}
-const LRU_SKIP: bindings::lru_status = bindings::lru_status_LRU_SKIP;
-const LRU_REMOVED_ENTRY: bindings::lru_status = bindings::lru_status_LRU_REMOVED_RETRY;
+const LRU_SKIP: bindings::lru_status = bindings::lru_status::LRU_SKIP;
+const LRU_REMOVED_ENTRY: bindings::lru_status = bindings::lru_status::LRU_REMOVED_RETRY;
/// # Safety
/// Called by the shrinker.
diff --git a/drivers/android/binder/page_range_helper.c b/drivers/android/binder/page_range_helper.c
deleted file mode 100644
index 496887723ee003e910d6ce67dbadd8c5286e39d1..0000000000000000000000000000000000000000
--- a/drivers/android/binder/page_range_helper.c
+++ /dev/null
@@ -1,24 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/* C helper for page_range.rs to work around a CFI violation.
- *
- * Bindgen currently pretends that `enum lru_status` is the same as an integer.
- * This assumption is fine ABI-wise, but once you add CFI to the mix, it
- * triggers a CFI violation because `enum lru_status` gets a different CFI tag.
- *
- * This file contains a workaround until bindgen can be fixed.
- *
- * Copyright (C) 2025 Google LLC.
- */
-#include "page_range_helper.h"
-
-unsigned int rust_shrink_free_page(struct list_head *item,
- struct list_lru_one *list,
- void *cb_arg);
-
-enum lru_status
-rust_shrink_free_page_wrap(struct list_head *item, struct list_lru_one *list,
- void *cb_arg)
-{
- return rust_shrink_free_page(item, list, cb_arg);
-}
diff --git a/drivers/android/binder/page_range_helper.h b/drivers/android/binder/page_range_helper.h
deleted file mode 100644
index 18dd2dd117b253fcbac735b48032b8f2d53d11fe..0000000000000000000000000000000000000000
--- a/drivers/android/binder/page_range_helper.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Copyright (C) 2025 Google, Inc.
- */
-
-#ifndef _LINUX_PAGE_RANGE_HELPER_H
-#define _LINUX_PAGE_RANGE_HELPER_H
-
-#include <linux/list_lru.h>
-
-enum lru_status
-rust_shrink_free_page_wrap(struct list_head *item, struct list_lru_one *list,
- void *cb_arg);
-
-#endif /* _LINUX_PAGE_RANGE_HELPER_H */
diff --git a/rust/bindgen_parameters b/rust/bindgen_parameters
index fd2fd1c3cb9a51ea46fcd721907783b457aa1378..1358f3348ffdd31f9bef6c04ee9577d0f6a0c5a6 100644
--- a/rust/bindgen_parameters
+++ b/rust/bindgen_parameters
@@ -23,6 +23,10 @@
# warning. We don't need to peek into it anyway.
--opaque-type spinlock
+# enums that appear in indirect function calls should specify a cfi type
+--newtype-enum lru_status
+--with-attribute-custom-enum=lru_status='#[cfi_encoding="lru_status"]'
+
# `seccomp`'s comment gets understood as a doctest
--no-doc-comments
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index a067038b4b422b4256f4a2b75fe644d47e6e82c8..cc12cf1614eac38bcbc1c634e6e7e6d8ccfec434 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -147,5 +147,4 @@ const vm_flags_t RUST_CONST_HELPER_VM_NOHUGEPAGE = VM_NOHUGEPAGE;
#if IS_ENABLED(CONFIG_ANDROID_BINDER_IPC_RUST)
#include "../../drivers/android/binder/rust_binder.h"
#include "../../drivers/android/binder/rust_binder_events.h"
-#include "../../drivers/android/binder/page_range_helper.h"
#endif
diff --git a/rust/bindings/lib.rs b/rust/bindings/lib.rs
index 0c57cf9b4004f176997c59ecc58a9a9ac76163d9..7f72ab66eebe6ef4227ce1b210d66a7867cbf5dd 100644
--- a/rust/bindings/lib.rs
+++ b/rust/bindings/lib.rs
@@ -23,6 +23,7 @@
unreachable_pub,
unsafe_op_in_unsafe_fn
)]
+#![feature(cfi_encoding)]
#[allow(dead_code)]
#[allow(clippy::cast_lossless)]
diff --git a/rust/uapi/lib.rs b/rust/uapi/lib.rs
index 1d5fd9efb93e9db97fec84fca2bae37b500c20c5..83c4795acbff1da852639bcbd9bcf5fb66b7e070 100644
--- a/rust/uapi/lib.rs
+++ b/rust/uapi/lib.rs
@@ -28,6 +28,7 @@
unsafe_op_in_unsafe_fn
)]
#![cfg_attr(CONFIG_RUSTC_HAS_UNNECESSARY_TRANSMUTES, allow(unnecessary_transmutes))]
+#![feature(cfi_encoding)]
// Manual definition of blocklisted types.
type __kernel_size_t = usize;
---
base-commit: 9ace4753a5202b02191d54e9fdf7f9e3d02b85eb
change-id: 20260105-cfi-lru-status-60d05fe6f93b
Best regards,
--
Alice Ryhl <aliceryhl@google.com>
On Mon, Jan 05, 2026 at 04:12:47PM +0000, Alice Ryhl wrote: > By default bindgen will convert 'enum lru_status' into a typedef for an > integer, but this leads to the wrong cfi type. It's supposed to be a > type called "lru_status" rather than the underlying native integer type. Is this a bug in bindgen? -- Kees Cook
On Tue, Jan 6, 2026 at 12:01 PM Kees Cook <kees@kernel.org> wrote:
>
> On Mon, Jan 05, 2026 at 04:12:47PM +0000, Alice Ryhl wrote:
> > By default bindgen will convert 'enum lru_status' into a typedef for an
> > integer, but this leads to the wrong cfi type. It's supposed to be a
> > type called "lru_status" rather than the underlying native integer type.
>
> Is this a bug in bindgen?
Kind of, kind of not. The things that are interacting here:
Rust assumes that enums have all their variants defined in its
codegen. This means that if the Rust definition for an integer enum
has variants A=0, B=1, and C=2, and we get sent `0xffff` from C,
undefined behavior occurs. To mitigate that, `bindgen` usually (unless
explicitly told otherwise) *does not* represent C enums as Rust enums.
There are several ways to do this. The default, usually used in the
kernel, is to represent them as integers of the correct size with a
set of constants available to compare them against. Alice has added an
exception for this type which switches it to use
`#[repr(transparent)]` struct lru_status(int_of_appropriate_width)` as
the type.
The Rust CFI implementation assumes:
1. `#[repr(C)] struct MyStruct {` should have an encoding equivalent
to a C type named `MyStruct` in the root namespace, similar to using
`extern "C" {` in C++.
2. `#[repr(transparent)] struct Foo {` uses an encoding equivalent to
that of its only non-zero-sized field. This is done because the usual
reason for `#[repr(transparent)]` is to guarantee that the layout is
identical to that field so you can safely transmute between the struct
and its field in certain scenarios. There are several places in
commonly used Rust libraries where CFI will report if
`#[repr(transparent)] struct Foo { bar: Bar }` is not encoded
equivalently to `Bar` as a result.
If `bindgen` output `#[repr(C)] struct
lru_status(int_of_appropriate_width)`, it would just work, since that
encodes as a C struct named `lru_status`, which happens to be the same
as a C enum named `lru_status`. However, outside the scope of CFI,
labeling this as `#[repr(C)]` rather than `#[repr(transparent)]` makes
no sense.
What Alice is doing would create a struct with `#[cfi_encoding =
"myencoding"] #[repr(transparent)] struct lru_status(i32);`, which
would get treated as a transparent struct, but have the provided
encoding.
The possible fixes in bindgen would be to provide
yet-another-enum-generation-mode which causes `#[repr(C)] struct
lru_status(` to be generated (easiest) or to automatically attach
`#[cfi_encoding = "correctencoding"]` for all declared types, possibly
by querying clang first. This latter approach could theoretically come
with an error to emit if CFI encodings were being generated but a type
that would need one is using a raw integer (which can't have a CFI
encoding attached).
>
> --
> Kees Cook
On Tue, Jan 06, 2026 at 12:18:41PM -0800, Matthew Maurer wrote:
> On Tue, Jan 6, 2026 at 12:01 PM Kees Cook <kees@kernel.org> wrote:
> >
> > On Mon, Jan 05, 2026 at 04:12:47PM +0000, Alice Ryhl wrote:
> > > By default bindgen will convert 'enum lru_status' into a typedef for an
> > > integer, but this leads to the wrong cfi type. It's supposed to be a
> > > type called "lru_status" rather than the underlying native integer type.
> >
> > Is this a bug in bindgen?
>
> Kind of, kind of not. The things that are interacting here:
>
> Rust assumes that enums have all their variants defined in its
> codegen. This means that if the Rust definition for an integer enum
> has variants A=0, B=1, and C=2, and we get sent `0xffff` from C,
> undefined behavior occurs. To mitigate that, `bindgen` usually (unless
> explicitly told otherwise) *does not* represent C enums as Rust enums.
> There are several ways to do this. The default, usually used in the
> kernel, is to represent them as integers of the correct size with a
> set of constants available to compare them against. Alice has added an
> exception for this type which switches it to use
> `#[repr(transparent)]` struct lru_status(int_of_appropriate_width)` as
> the type.
>
> The Rust CFI implementation assumes:
>
> 1. `#[repr(C)] struct MyStruct {` should have an encoding equivalent
> to a C type named `MyStruct` in the root namespace, similar to using
> `extern "C" {` in C++.
> 2. `#[repr(transparent)] struct Foo {` uses an encoding equivalent to
> that of its only non-zero-sized field. This is done because the usual
> reason for `#[repr(transparent)]` is to guarantee that the layout is
> identical to that field so you can safely transmute between the struct
> and its field in certain scenarios. There are several places in
> commonly used Rust libraries where CFI will report if
> `#[repr(transparent)] struct Foo { bar: Bar }` is not encoded
> equivalently to `Bar` as a result.
>
> If `bindgen` output `#[repr(C)] struct
> lru_status(int_of_appropriate_width)`, it would just work, since that
> encodes as a C struct named `lru_status`, which happens to be the same
> as a C enum named `lru_status`. However, outside the scope of CFI,
> labeling this as `#[repr(C)]` rather than `#[repr(transparent)]` makes
> no sense.
>
> What Alice is doing would create a struct with `#[cfi_encoding =
> "myencoding"] #[repr(transparent)] struct lru_status(i32);`, which
> would get treated as a transparent struct, but have the provided
> encoding.
>
> The possible fixes in bindgen would be to provide
> yet-another-enum-generation-mode which causes `#[repr(C)] struct
> lru_status(` to be generated (easiest) or to automatically attach
> `#[cfi_encoding = "correctencoding"]` for all declared types, possibly
> by querying clang first. This latter approach could theoretically come
> with an error to emit if CFI encodings were being generated but a type
> that would need one is using a raw integer (which can't have a CFI
> encoding attached).
I see; thanks for the detailed explanation! My main reason for asking
about this was wondering if this was going to be something that needed
to be fixed for all enums, or if this was more of a one-off. It sounds
like it's basically a one-off due to how the enum was chosen to be
mapped in this case, so that's less concerning. I guess we just need to
make note of this when choosing that kind of mapping in the future?
--
Kees Cook
On Tue, Jan 6, 2026 at 12:25 PM Kees Cook <kees@kernel.org> wrote:
>
> On Tue, Jan 06, 2026 at 12:18:41PM -0800, Matthew Maurer wrote:
> > On Tue, Jan 6, 2026 at 12:01 PM Kees Cook <kees@kernel.org> wrote:
> > >
> > > On Mon, Jan 05, 2026 at 04:12:47PM +0000, Alice Ryhl wrote:
> > > > By default bindgen will convert 'enum lru_status' into a typedef for an
> > > > integer, but this leads to the wrong cfi type. It's supposed to be a
> > > > type called "lru_status" rather than the underlying native integer type.
> > >
> > > Is this a bug in bindgen?
> >
> > Kind of, kind of not. The things that are interacting here:
> >
> > Rust assumes that enums have all their variants defined in its
> > codegen. This means that if the Rust definition for an integer enum
> > has variants A=0, B=1, and C=2, and we get sent `0xffff` from C,
> > undefined behavior occurs. To mitigate that, `bindgen` usually (unless
> > explicitly told otherwise) *does not* represent C enums as Rust enums.
> > There are several ways to do this. The default, usually used in the
> > kernel, is to represent them as integers of the correct size with a
> > set of constants available to compare them against. Alice has added an
> > exception for this type which switches it to use
> > `#[repr(transparent)]` struct lru_status(int_of_appropriate_width)` as
> > the type.
> >
> > The Rust CFI implementation assumes:
> >
> > 1. `#[repr(C)] struct MyStruct {` should have an encoding equivalent
> > to a C type named `MyStruct` in the root namespace, similar to using
> > `extern "C" {` in C++.
> > 2. `#[repr(transparent)] struct Foo {` uses an encoding equivalent to
> > that of its only non-zero-sized field. This is done because the usual
> > reason for `#[repr(transparent)]` is to guarantee that the layout is
> > identical to that field so you can safely transmute between the struct
> > and its field in certain scenarios. There are several places in
> > commonly used Rust libraries where CFI will report if
> > `#[repr(transparent)] struct Foo { bar: Bar }` is not encoded
> > equivalently to `Bar` as a result.
> >
> > If `bindgen` output `#[repr(C)] struct
> > lru_status(int_of_appropriate_width)`, it would just work, since that
> > encodes as a C struct named `lru_status`, which happens to be the same
> > as a C enum named `lru_status`. However, outside the scope of CFI,
> > labeling this as `#[repr(C)]` rather than `#[repr(transparent)]` makes
> > no sense.
> >
> > What Alice is doing would create a struct with `#[cfi_encoding =
> > "myencoding"] #[repr(transparent)] struct lru_status(i32);`, which
> > would get treated as a transparent struct, but have the provided
> > encoding.
> >
> > The possible fixes in bindgen would be to provide
> > yet-another-enum-generation-mode which causes `#[repr(C)] struct
> > lru_status(` to be generated (easiest) or to automatically attach
> > `#[cfi_encoding = "correctencoding"]` for all declared types, possibly
> > by querying clang first. This latter approach could theoretically come
> > with an error to emit if CFI encodings were being generated but a type
> > that would need one is using a raw integer (which can't have a CFI
> > encoding attached).
>
> I see; thanks for the detailed explanation! My main reason for asking
> about this was wondering if this was going to be something that needed
> to be fixed for all enums, or if this was more of a one-off.
It needs to be fixed in some form for any enum which appears in a
function pointer type where Rust either calls the function pointer or
defines a function that will be used as a function pointer. It would
be best if we got a comprehensive solution at some point (either all
`#[repr(C)]` or always attaching `#[cfi_encoding]`). It just isn't
common at the moment in C APIs to combine function pointers and `enum
foo`.
> It sounds
> like it's basically a one-off due to how the enum was chosen to be
> mapped in this case, so that's less concerning. I guess we just need to
> make note of this when choosing that kind of mapping in the future?
Arguably the C code triggering this is doing the right thing by
referring to the type as `enum foo` rather than `int`. None of the
default Rust representations in `bindgen` will get us a compatible
representation, which results in things like people writing a manual
`#[cfi_encoding]`, so in an ideal world I would like to see something
done in bindgen about this.
>
> --
> Kees Cook
On Mon, Jan 5, 2026 at 8:12 AM Alice Ryhl <aliceryhl@google.com> wrote:
>
> By default bindgen will convert 'enum lru_status' into a typedef for an
> integer, but this leads to the wrong cfi type. It's supposed to be a
> type called "lru_status" rather than the underlying native integer type.
>
> To fix this, tell bindgen to generate a newtype and set the CFI type
> explicitly. Note that we need to set the CFI attribute explicitly as
> bindgen is using repr(transparent), which is otherwise identical to the
> inner type for ABI purposes.
>
> This allows us to remove the page range helper C function in Binder
> without risking a CFI failure when list_lru_walk calls the provided
> function pointer.
>
> This requires bindgen v0.71 or greater.
>
> Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> ---
> drivers/android/binder/Makefile | 3 +--
> drivers/android/binder/page_range.rs | 6 +++---
> drivers/android/binder/page_range_helper.c | 24 ------------------------
> drivers/android/binder/page_range_helper.h | 15 ---------------
> rust/bindgen_parameters | 4 ++++
> rust/bindings/bindings_helper.h | 1 -
> rust/bindings/lib.rs | 1 +
> rust/uapi/lib.rs | 1 +
> 8 files changed, 10 insertions(+), 45 deletions(-)
>
> diff --git a/drivers/android/binder/Makefile b/drivers/android/binder/Makefile
> index 09eabb527fa092b659559367705fd3667db6cb2c..7e0cd9782a8b24db598034e15e5a36eca91b3fa9 100644
> --- a/drivers/android/binder/Makefile
> +++ b/drivers/android/binder/Makefile
> @@ -5,5 +5,4 @@ obj-$(CONFIG_ANDROID_BINDER_IPC_RUST) += rust_binder.o
> rust_binder-y := \
> rust_binder_main.o \
> rust_binderfs.o \
> - rust_binder_events.o \
> - page_range_helper.o
> + rust_binder_events.o
> diff --git a/drivers/android/binder/page_range.rs b/drivers/android/binder/page_range.rs
> index 9379038f61f513c51ebed6c7e7b6fde32e5b8d06..eb738e169525839a199132dd71e69e0b9cc69053 100644
> --- a/drivers/android/binder/page_range.rs
> +++ b/drivers/android/binder/page_range.rs
> @@ -642,15 +642,15 @@ fn drop(self: Pin<&mut Self>) {
> unsafe {
> bindings::list_lru_walk(
> list_lru,
> - Some(bindings::rust_shrink_free_page_wrap),
> + Some(rust_shrink_free_page),
> ptr::null_mut(),
> nr_to_scan,
> )
> }
> }
>
> -const LRU_SKIP: bindings::lru_status = bindings::lru_status_LRU_SKIP;
> -const LRU_REMOVED_ENTRY: bindings::lru_status = bindings::lru_status_LRU_REMOVED_RETRY;
> +const LRU_SKIP: bindings::lru_status = bindings::lru_status::LRU_SKIP;
> +const LRU_REMOVED_ENTRY: bindings::lru_status = bindings::lru_status::LRU_REMOVED_RETRY;
>
> /// # Safety
> /// Called by the shrinker.
> diff --git a/drivers/android/binder/page_range_helper.c b/drivers/android/binder/page_range_helper.c
> deleted file mode 100644
> index 496887723ee003e910d6ce67dbadd8c5286e39d1..0000000000000000000000000000000000000000
> --- a/drivers/android/binder/page_range_helper.c
> +++ /dev/null
> @@ -1,24 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0
> -
> -/* C helper for page_range.rs to work around a CFI violation.
> - *
> - * Bindgen currently pretends that `enum lru_status` is the same as an integer.
> - * This assumption is fine ABI-wise, but once you add CFI to the mix, it
> - * triggers a CFI violation because `enum lru_status` gets a different CFI tag.
> - *
> - * This file contains a workaround until bindgen can be fixed.
> - *
> - * Copyright (C) 2025 Google LLC.
> - */
> -#include "page_range_helper.h"
> -
> -unsigned int rust_shrink_free_page(struct list_head *item,
> - struct list_lru_one *list,
> - void *cb_arg);
> -
> -enum lru_status
> -rust_shrink_free_page_wrap(struct list_head *item, struct list_lru_one *list,
> - void *cb_arg)
> -{
> - return rust_shrink_free_page(item, list, cb_arg);
> -}
> diff --git a/drivers/android/binder/page_range_helper.h b/drivers/android/binder/page_range_helper.h
> deleted file mode 100644
> index 18dd2dd117b253fcbac735b48032b8f2d53d11fe..0000000000000000000000000000000000000000
> --- a/drivers/android/binder/page_range_helper.h
> +++ /dev/null
> @@ -1,15 +0,0 @@
> -/* SPDX-License-Identifier: GPL-2.0 */
> -/*
> - * Copyright (C) 2025 Google, Inc.
> - */
> -
> -#ifndef _LINUX_PAGE_RANGE_HELPER_H
> -#define _LINUX_PAGE_RANGE_HELPER_H
> -
> -#include <linux/list_lru.h>
> -
> -enum lru_status
> -rust_shrink_free_page_wrap(struct list_head *item, struct list_lru_one *list,
> - void *cb_arg);
> -
> -#endif /* _LINUX_PAGE_RANGE_HELPER_H */
> diff --git a/rust/bindgen_parameters b/rust/bindgen_parameters
> index fd2fd1c3cb9a51ea46fcd721907783b457aa1378..1358f3348ffdd31f9bef6c04ee9577d0f6a0c5a6 100644
> --- a/rust/bindgen_parameters
> +++ b/rust/bindgen_parameters
> @@ -23,6 +23,10 @@
> # warning. We don't need to peek into it anyway.
> --opaque-type spinlock
>
> +# enums that appear in indirect function calls should specify a cfi type
> +--newtype-enum lru_status
I think this may need to be `#[cfi_encoding="10lru_status"]`
> +--with-attribute-custom-enum=lru_status='#[cfi_encoding="lru_status"]'
> +
> # `seccomp`'s comment gets understood as a doctest
> --no-doc-comments
>
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index a067038b4b422b4256f4a2b75fe644d47e6e82c8..cc12cf1614eac38bcbc1c634e6e7e6d8ccfec434 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -147,5 +147,4 @@ const vm_flags_t RUST_CONST_HELPER_VM_NOHUGEPAGE = VM_NOHUGEPAGE;
> #if IS_ENABLED(CONFIG_ANDROID_BINDER_IPC_RUST)
> #include "../../drivers/android/binder/rust_binder.h"
> #include "../../drivers/android/binder/rust_binder_events.h"
> -#include "../../drivers/android/binder/page_range_helper.h"
> #endif
> diff --git a/rust/bindings/lib.rs b/rust/bindings/lib.rs
> index 0c57cf9b4004f176997c59ecc58a9a9ac76163d9..7f72ab66eebe6ef4227ce1b210d66a7867cbf5dd 100644
> --- a/rust/bindings/lib.rs
> +++ b/rust/bindings/lib.rs
> @@ -23,6 +23,7 @@
> unreachable_pub,
> unsafe_op_in_unsafe_fn
> )]
> +#![feature(cfi_encoding)]
>
> #[allow(dead_code)]
> #[allow(clippy::cast_lossless)]
> diff --git a/rust/uapi/lib.rs b/rust/uapi/lib.rs
> index 1d5fd9efb93e9db97fec84fca2bae37b500c20c5..83c4795acbff1da852639bcbd9bcf5fb66b7e070 100644
> --- a/rust/uapi/lib.rs
> +++ b/rust/uapi/lib.rs
> @@ -28,6 +28,7 @@
> unsafe_op_in_unsafe_fn
> )]
> #![cfg_attr(CONFIG_RUSTC_HAS_UNNECESSARY_TRANSMUTES, allow(unnecessary_transmutes))]
> +#![feature(cfi_encoding)]
>
> // Manual definition of blocklisted types.
> type __kernel_size_t = usize;
>
> ---
> base-commit: 9ace4753a5202b02191d54e9fdf7f9e3d02b85eb
> change-id: 20260105-cfi-lru-status-60d05fe6f93b
>
> Best regards,
> --
> Alice Ryhl <aliceryhl@google.com>
>
On Mon, Jan 05, 2026 at 09:19:53AM -0800, Matthew Maurer wrote:
> On Mon, Jan 5, 2026 at 8:12 AM Alice Ryhl <aliceryhl@google.com> wrote:
> >
> > By default bindgen will convert 'enum lru_status' into a typedef for an
> > integer, but this leads to the wrong cfi type. It's supposed to be a
> > type called "lru_status" rather than the underlying native integer type.
> >
> > To fix this, tell bindgen to generate a newtype and set the CFI type
> > explicitly. Note that we need to set the CFI attribute explicitly as
> > bindgen is using repr(transparent), which is otherwise identical to the
> > inner type for ABI purposes.
> >
> > This allows us to remove the page range helper C function in Binder
> > without risking a CFI failure when list_lru_walk calls the provided
> > function pointer.
> >
> > This requires bindgen v0.71 or greater.
> >
> > Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> > ---
> > drivers/android/binder/Makefile | 3 +--
> > drivers/android/binder/page_range.rs | 6 +++---
> > drivers/android/binder/page_range_helper.c | 24 ------------------------
> > drivers/android/binder/page_range_helper.h | 15 ---------------
> > rust/bindgen_parameters | 4 ++++
> > rust/bindings/bindings_helper.h | 1 -
> > rust/bindings/lib.rs | 1 +
> > rust/uapi/lib.rs | 1 +
> > 8 files changed, 10 insertions(+), 45 deletions(-)
> >
> > diff --git a/drivers/android/binder/Makefile b/drivers/android/binder/Makefile
> > index 09eabb527fa092b659559367705fd3667db6cb2c..7e0cd9782a8b24db598034e15e5a36eca91b3fa9 100644
> > --- a/drivers/android/binder/Makefile
> > +++ b/drivers/android/binder/Makefile
> > @@ -5,5 +5,4 @@ obj-$(CONFIG_ANDROID_BINDER_IPC_RUST) += rust_binder.o
> > rust_binder-y := \
> > rust_binder_main.o \
> > rust_binderfs.o \
> > - rust_binder_events.o \
> > - page_range_helper.o
> > + rust_binder_events.o
> > diff --git a/drivers/android/binder/page_range.rs b/drivers/android/binder/page_range.rs
> > index 9379038f61f513c51ebed6c7e7b6fde32e5b8d06..eb738e169525839a199132dd71e69e0b9cc69053 100644
> > --- a/drivers/android/binder/page_range.rs
> > +++ b/drivers/android/binder/page_range.rs
> > @@ -642,15 +642,15 @@ fn drop(self: Pin<&mut Self>) {
> > unsafe {
> > bindings::list_lru_walk(
> > list_lru,
> > - Some(bindings::rust_shrink_free_page_wrap),
> > + Some(rust_shrink_free_page),
> > ptr::null_mut(),
> > nr_to_scan,
> > )
> > }
> > }
> >
> > -const LRU_SKIP: bindings::lru_status = bindings::lru_status_LRU_SKIP;
> > -const LRU_REMOVED_ENTRY: bindings::lru_status = bindings::lru_status_LRU_REMOVED_RETRY;
> > +const LRU_SKIP: bindings::lru_status = bindings::lru_status::LRU_SKIP;
> > +const LRU_REMOVED_ENTRY: bindings::lru_status = bindings::lru_status::LRU_REMOVED_RETRY;
> >
> > /// # Safety
> > /// Called by the shrinker.
> > diff --git a/drivers/android/binder/page_range_helper.c b/drivers/android/binder/page_range_helper.c
> > deleted file mode 100644
> > index 496887723ee003e910d6ce67dbadd8c5286e39d1..0000000000000000000000000000000000000000
> > --- a/drivers/android/binder/page_range_helper.c
> > +++ /dev/null
> > @@ -1,24 +0,0 @@
> > -// SPDX-License-Identifier: GPL-2.0
> > -
> > -/* C helper for page_range.rs to work around a CFI violation.
> > - *
> > - * Bindgen currently pretends that `enum lru_status` is the same as an integer.
> > - * This assumption is fine ABI-wise, but once you add CFI to the mix, it
> > - * triggers a CFI violation because `enum lru_status` gets a different CFI tag.
> > - *
> > - * This file contains a workaround until bindgen can be fixed.
> > - *
> > - * Copyright (C) 2025 Google LLC.
> > - */
> > -#include "page_range_helper.h"
> > -
> > -unsigned int rust_shrink_free_page(struct list_head *item,
> > - struct list_lru_one *list,
> > - void *cb_arg);
> > -
> > -enum lru_status
> > -rust_shrink_free_page_wrap(struct list_head *item, struct list_lru_one *list,
> > - void *cb_arg)
> > -{
> > - return rust_shrink_free_page(item, list, cb_arg);
> > -}
> > diff --git a/drivers/android/binder/page_range_helper.h b/drivers/android/binder/page_range_helper.h
> > deleted file mode 100644
> > index 18dd2dd117b253fcbac735b48032b8f2d53d11fe..0000000000000000000000000000000000000000
> > --- a/drivers/android/binder/page_range_helper.h
> > +++ /dev/null
> > @@ -1,15 +0,0 @@
> > -/* SPDX-License-Identifier: GPL-2.0 */
> > -/*
> > - * Copyright (C) 2025 Google, Inc.
> > - */
> > -
> > -#ifndef _LINUX_PAGE_RANGE_HELPER_H
> > -#define _LINUX_PAGE_RANGE_HELPER_H
> > -
> > -#include <linux/list_lru.h>
> > -
> > -enum lru_status
> > -rust_shrink_free_page_wrap(struct list_head *item, struct list_lru_one *list,
> > - void *cb_arg);
> > -
> > -#endif /* _LINUX_PAGE_RANGE_HELPER_H */
> > diff --git a/rust/bindgen_parameters b/rust/bindgen_parameters
> > index fd2fd1c3cb9a51ea46fcd721907783b457aa1378..1358f3348ffdd31f9bef6c04ee9577d0f6a0c5a6 100644
> > --- a/rust/bindgen_parameters
> > +++ b/rust/bindgen_parameters
> > @@ -23,6 +23,10 @@
> > # warning. We don't need to peek into it anyway.
> > --opaque-type spinlock
> >
> > +# enums that appear in indirect function calls should specify a cfi type
> > +--newtype-enum lru_status
> > +--with-attribute-custom-enum=lru_status='#[cfi_encoding="lru_status"]'
>
> I think this may need to be `#[cfi_encoding="10lru_status"]`
Ah, I did have some trouble testing this.
Is there an easy way to test that I got the cfi enconding right? I guess
I can always add a kunit test that makes a dynamic function call...
Alice
© 2016 - 2026 Red Hat, Inc.