Uh oh!
There was an error while loading. Please reload this page.
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.
TheSven73
commented
Jun 5, 2021
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 |
nbdd0121
commented
Jun 5, 2021
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"{fnerrname(err: c_types::c_int) -> *const c_types::c_char;}which will cause #definerust_helper_errnamewhich means in #ifndefrust_helper_errnameconstchar*rust_helper_errname(interr)
{
returnerrname(err);
}
EXPORT_SYMBOL_GPL(rust_helper_errname);
#endifOn the other hand, if it's defined as macro or static inline function, extern"C"{fnrust_helper_errname(err: c_types::c_int) -> *const c_types::c_char;}which I use extern"C"{#[link_name = "rust_helper_errname"]fnerrname(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.
nbdd0121
commented
Jul 2, 2021
v1 -> v2changed the mechanism used to prevent name conflict
|
ksquirrel
commented
Jul 2, 2021
Review of
|
fbq
commented
Jul 6, 2021
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: boolrust_helper_do_something()and use it as ifunsafe{ bindings::do_something()}{
<xxx>
}Then some one, who is not aware of our implementation and usage, also implement a booldo_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? |
bjorn3
commented
Jul 6, 2021
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. |
nbdd0121
commented
Jul 6, 2021
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 |
fbq
commented
Jul 6, 2021
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. |
fbq
commented
Jul 6, 2021
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?
|
nbdd0121
commented
Jul 29, 2021
Sounds very reasonable to me. |
nbdd0121
commented
Jul 29, 2021
Rebased, and changed |
ojeda
commented
Aug 24, 2021
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 <gary@garyguo.net>
ojeda
commented
Aug 24, 2021
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() };
} |
nbdd0121
commented
Aug 24, 2021
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 |
ojeda
commented
Aug 24, 2021
I think we crossed messages -- see my previous one. Please add the |
nbdd0121
commented
Aug 24, 2021
Ok. I keep the One single additional instruction on the perhaps coldest code path.. I am not worried. It could be removed with |
nbdd0121
commented
Aug 24, 2021
I think the change exposes a bug in |
Signed-off-by: Gary Guo <gary@garyguo.net>
Signed-off-by: Gary Guo <gary@garyguo.net>
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 <pablo@netfilter.org>
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::fooregardlesswhether
foois directly exposed or exposed via a helper.Details about how this works:
bindings_generated.rsis first created via bindgen;bindings_generated.rsand arust_helper_foomacro is generated for each function directlyexposed this way;
helpers.ccan then use C preprocessor to conditionally compilehelpers only if they are not directly exposed.
helpers.c.rust_helperprefix and add
#[link_name = ""].Related #352