-
Notifications
You must be signed in to change notification settings - Fork 436
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
rust: generate bindings for helpers #359
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
I'm not sure I get what this does... could you provide a layperson description ? :) If I understand correctly, we'd still have to create rust helpers manually in |
Yes, and if the header exposes functions as extern symbols instead of macro or It's not magic :). Here's some detailed example. We know that If it is an extern function, then extern "C" {
fn errname(err: c_types::c_int) -> *const c_types::c_char;
} which will cause #define rust_helper_errname which means in #ifndef rust_helper_errname
const char *rust_helper_errname(int err)
{
return errname(err);
}
EXPORT_SYMBOL_GPL(rust_helper_errname);
#endif On the other hand, if it's defined as macro or static inline function, extern "C" {
fn rust_helper_errname(err: c_types::c_int) -> *const c_types::c_char;
} which I use extern "C" {
#[link_name = "rust_helper_errname"]
fn errname(err: c_types::c_int) -> *const c_types::c_char;
} So the caller don't need to care about whether an extern symbol exists or a helper is needed; it just calls |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
v1 -> v2changed the mechanism used to prevent name conflict
|
Review of
|
I like the idea that boilerplate "extern"s can be removed ;-) But I think at least we want the helpers to have different namespaces with other bindings, consider the following case: First we implement a helper named: bool rust_helper_do_something() and use it as if unsafe { bindings::do_something() } {
<xxx>
} Then some one, who is not aware of our implementation and usage, also implement a bool do_something()
{
...
}
EXPORT_SYM(do_domething); And it will be preferred by Rust code, so here comes the first problem, what if the C version of And there is another problem, if both Rust side code and C side code got evolved a bit around Thoughts? |
Helpers are normally named after the macro they call. If a macro and a C function have the same name, that gives problems anyway as the macro shadows the function amd if you include the header with the macro before the header with the function, you get a syntax error or renamed function. |
I would consider it very problematic and confusing if a It's a deliberate choice to use the same namespace, so that the Rust users do not have to care whether the function is directly exposed or exposed via a helper. Some functions like |
Well, you said "normally", so there are exceptions, and the thing is, we don't have a way to detect the exception (at least I don't see one in this PR). Luckily the only exception seems to be Having an undetectable problem is dangerous, and it's even more dangerous when the problem is normally rare. |
As long as you can guarantee that Now think about this, maybe we can try this if For example, instead of defining This means in rust/helpers, we either:
I think it's a simple and practical rule to follow when we add things in rust/helpers.c, and rules out some potential issues because of sharing namespace with normal bindings. Thoughts?
|
Sounds very reasonable to me. |
Rebased, and changed |
Solving the conflicts and trying this one, we get a warning about a clashing
|
Automatically generate rust bindings for helper functions via bindgen. The corresponding Rust bindings will have their `rust_helper_` prefix removed, so Rust code can call `bindings::foo` regardless whether `foo` is directly exposed or exposed via a helper. When both a directly exposed symbol and a helper exists, the directly exposed symbol will take precedence. Signed-off-by: Gary Guo <[email protected]>
Perhaps we should have "manual helpers" for bits like this, something like: diff --git a/rust/helpers.c b/rust/helpers.c
index b74c8991fb9..a6ef4cd02e0 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -13,7 +13,9 @@
#include <linux/security.h>
#include <asm/io.h>
-void rust_helper_BUG(void)
+// Manual until `bindgen` supports `__noreturn`
+// https://github.com/rust-lang/rust-bindgen/issues/2094
+__noreturn void rust_manual_helper_BUG(void)
{
BUG();
}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index c6bf9043afb..a95c4f57e8c 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -233,9 +233,9 @@ macro_rules! container_of {
#[panic_handler]
fn panic(info: &core::panic::PanicInfo<'_>) -> ! {
extern "C" {
- fn rust_helper_BUG() -> !;
+ fn rust_manual_helper_BUG() -> !;
}
pr_emerg!("{}\n", info);
// SAFETY: FFI call.
- unsafe { rust_helper_BUG() };
+ unsafe { rust_manual_helper_BUG() };
} |
Rebased and fixed conflicts. I think given that there aren't many noreturn ffi functions, we can just live with it for now. I've added a |
I think we crossed messages -- see my previous one. Please add the |
Ok. I keep the One single additional instruction on the perhaps coldest code path.. I am not worried. It could be removed with |
I think the change exposes a bug in |
Signed-off-by: Gary Guo <[email protected]>
Signed-off-by: Gary Guo <[email protected]>
Similar to 2c9f029 ("netfilter: nf_tables: flush pending destroy work before netlink notifier") to address a race between exit_net and the destroy workqueue. The trace below shows an element to be released via destroy workqueue while exit_net path (triggered via module removal) has already released the set that is used in such transaction. [ 1360.547789] BUG: KASAN: slab-use-after-free in nf_tables_trans_destroy_work+0x3f5/0x590 [nf_tables] [ 1360.547861] Read of size 8 at addr ffff888140500cc0 by task kworker/4:1/152465 [ 1360.547870] CPU: 4 PID: 152465 Comm: kworker/4:1 Not tainted 6.8.0+ Rust-for-Linux#359 [ 1360.547882] Workqueue: events nf_tables_trans_destroy_work [nf_tables] [ 1360.547984] Call Trace: [ 1360.547991] <TASK> [ 1360.547998] dump_stack_lvl+0x53/0x70 [ 1360.548014] print_report+0xc4/0x610 [ 1360.548026] ? __virt_addr_valid+0xba/0x160 [ 1360.548040] ? __pfx__raw_spin_lock_irqsave+0x10/0x10 [ 1360.548054] ? nf_tables_trans_destroy_work+0x3f5/0x590 [nf_tables] [ 1360.548176] kasan_report+0xae/0xe0 [ 1360.548189] ? nf_tables_trans_destroy_work+0x3f5/0x590 [nf_tables] [ 1360.548312] nf_tables_trans_destroy_work+0x3f5/0x590 [nf_tables] [ 1360.548447] ? __pfx_nf_tables_trans_destroy_work+0x10/0x10 [nf_tables] [ 1360.548577] ? _raw_spin_unlock_irq+0x18/0x30 [ 1360.548591] process_one_work+0x2f1/0x670 [ 1360.548610] worker_thread+0x4d3/0x760 [ 1360.548627] ? __pfx_worker_thread+0x10/0x10 [ 1360.548640] kthread+0x16b/0x1b0 [ 1360.548653] ? __pfx_kthread+0x10/0x10 [ 1360.548665] ret_from_fork+0x2f/0x50 [ 1360.548679] ? __pfx_kthread+0x10/0x10 [ 1360.548690] ret_from_fork_asm+0x1a/0x30 [ 1360.548707] </TASK> [ 1360.548719] Allocated by task 192061: [ 1360.548726] kasan_save_stack+0x20/0x40 [ 1360.548739] kasan_save_track+0x14/0x30 [ 1360.548750] __kasan_kmalloc+0x8f/0xa0 [ 1360.548760] __kmalloc_node+0x1f1/0x450 [ 1360.548771] nf_tables_newset+0x10c7/0x1b50 [nf_tables] [ 1360.548883] nfnetlink_rcv_batch+0xbc4/0xdc0 [nfnetlink] [ 1360.548909] nfnetlink_rcv+0x1a8/0x1e0 [nfnetlink] [ 1360.548927] netlink_unicast+0x367/0x4f0 [ 1360.548935] netlink_sendmsg+0x34b/0x610 [ 1360.548944] ____sys_sendmsg+0x4d4/0x510 [ 1360.548953] ___sys_sendmsg+0xc9/0x120 [ 1360.548961] __sys_sendmsg+0xbe/0x140 [ 1360.548971] do_syscall_64+0x55/0x120 [ 1360.548982] entry_SYSCALL_64_after_hwframe+0x55/0x5d [ 1360.548994] Freed by task 192222: [ 1360.548999] kasan_save_stack+0x20/0x40 [ 1360.549009] kasan_save_track+0x14/0x30 [ 1360.549019] kasan_save_free_info+0x3b/0x60 [ 1360.549028] poison_slab_object+0x100/0x180 [ 1360.549036] __kasan_slab_free+0x14/0x30 [ 1360.549042] kfree+0xb6/0x260 [ 1360.549049] __nft_release_table+0x473/0x6a0 [nf_tables] [ 1360.549131] nf_tables_exit_net+0x170/0x240 [nf_tables] [ 1360.549221] ops_exit_list+0x50/0xa0 [ 1360.549229] free_exit_list+0x101/0x140 [ 1360.549236] unregister_pernet_operations+0x107/0x160 [ 1360.549245] unregister_pernet_subsys+0x1c/0x30 [ 1360.549254] nf_tables_module_exit+0x43/0x80 [nf_tables] [ 1360.549345] __do_sys_delete_module+0x253/0x370 [ 1360.549352] do_syscall_64+0x55/0x120 [ 1360.549360] entry_SYSCALL_64_after_hwframe+0x55/0x5d (gdb) list *__nft_release_table+0x473 0x1e033 is in __nft_release_table (net/netfilter/nf_tables_api.c:11354). 11349 list_for_each_entry_safe(flowtable, nf, &table->flowtables, list) { 11350 list_del(&flowtable->list); 11351 nft_use_dec(&table->use); 11352 nf_tables_flowtable_destroy(flowtable); 11353 } 11354 list_for_each_entry_safe(set, ns, &table->sets, list) { 11355 list_del(&set->list); 11356 nft_use_dec(&table->use); 11357 if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT)) 11358 nft_map_deactivate(&ctx, set); (gdb) [ 1360.549372] Last potentially related work creation: [ 1360.549376] kasan_save_stack+0x20/0x40 [ 1360.549384] __kasan_record_aux_stack+0x9b/0xb0 [ 1360.549392] __queue_work+0x3fb/0x780 [ 1360.549399] queue_work_on+0x4f/0x60 [ 1360.549407] nft_rhash_remove+0x33b/0x340 [nf_tables] [ 1360.549516] nf_tables_commit+0x1c6a/0x2620 [nf_tables] [ 1360.549625] nfnetlink_rcv_batch+0x728/0xdc0 [nfnetlink] [ 1360.549647] nfnetlink_rcv+0x1a8/0x1e0 [nfnetlink] [ 1360.549671] netlink_unicast+0x367/0x4f0 [ 1360.549680] netlink_sendmsg+0x34b/0x610 [ 1360.549690] ____sys_sendmsg+0x4d4/0x510 [ 1360.549697] ___sys_sendmsg+0xc9/0x120 [ 1360.549706] __sys_sendmsg+0xbe/0x140 [ 1360.549715] do_syscall_64+0x55/0x120 [ 1360.549725] entry_SYSCALL_64_after_hwframe+0x55/0x5d Fixes: 0935d55 ("netfilter: nf_tables: asynchronous release") Signed-off-by: Pablo Neira Ayuso <[email protected]>
Helper functions are now only compiled if the wrapped function is not
made directly available via bindgen. For the compiled helper functions,
bindings for them are automatically generated via a second invocation
to bindgen, and the corresponding binding will have its
rust_helper_
prefix removed, so Rust code can call
bindings::foo
regardlesswhether
foo
is directly exposed or exposed via a helper.Details about how this works:
bindings_generated.rs
is first created via bindgen;bindings_generated.rs
and arust_helper_foo
macro is generated for each function directlyexposed this way;
helpers.c
can then use C preprocessor to conditionally compilehelpers only if they are not directly exposed.
helpers.c
.rust_helper
prefix and add
#[link_name = ""]
.Related #352