-
Notifications
You must be signed in to change notification settings - Fork 135
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
Mark set_print as unsafe #374
Conversation
f524c8f
to
637eb8b
Compare
637eb8b
to
c9bd352
Compare
There is nothing unsfae about set_print just because it's not thread-safe, or am I missing something? Should any non-synchronized API be marked unsafe? That seems weird. Please don't do this. |
Thanks for the pull request, @mendess! I don't believe I follow the argumentation in favor of making it Unsafety, in the Rust sense, refers to violation of data race freedom and or memory safety (out of bounds accesses etc.). I don't believe either is possible here. Can you please clarify why you believe this to be necessary? Edit: Ah, sorry, mid-air collision with Andrii's post, which basically says the same thing. |
if it mutates a global variable then, per rust's rules, yes This api will mutate a global variable in C, global variable which will in turn be read by almost all the functions in If this doesn't make sense please tell me where I made the mistake. |
The point is (as discussed at length here) that the C stuff you are referring to effectively maps to Rust's AtomicUsize. And you can indeed use it safely in a global context: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=89e2b5d8074073e1f2727efa9810894c |
according to the C standard, In section 5.1.2.4 "Multi-threaded executions and data races" you can read this in paragraph 25:
In paragraph 4 you can get the definition of "conflict":
In paragraph 5 you can get the definition of "atomic operation", which doesn't include "word sized, aligned writes/reads". So according to the standard it's undefined behavior to have a read and a write to the same location from different threads when no ordering is specified. Now, I know that on x86/x64 architectures they might actually be atomic, but that only applies to the assembly generated, when you're writing C you're writing code for the C abstract machine, which has an optimizer that can reorder statements or even delete part of the code as I'm sure we all know. As such I believe you should always use true atomic operations when writing/reading to global memory locations. |
I agree (as I stated at least twice beforehand over in #245), which is why I advise you to bring this up with |
But this is not an implementation detail, the library itself states that
I agree that libbpf bugs should not be papered over here but this isn't a |
From #245 (comment):
So I agree that it appears as if this comment and this one contradict what was conveyed above. And I cannot reconcile the two without tea leaf reading (though I will say that I have a hunch...). So @anakryiko can you please confirm your intentions or clarify how I misrepresented what we discussed offline? |
I don't mind making libbpf_set_print() using atomic exchange internally. Let's do that change. We can even say that it's thread-safe at that point, though libbpf won't provide any strict ordering guarantees between libbpf_set_print() and seeing new print callback on another thread. Either way application shouldn't rely on such tiny races for correctness.
libbpf API not being thread-safe is a general statement about all the APIs. And that's true for most stuff (especially when we are talking about bpf_object and its methods, you definitely don't want to modify bpf_object state from two separate threads). But for stuff like One set of APIs where libbpf-rs probably might want to enforce synchronization is There might be few more APIs like this, though not very likely (I don't remember all of them off the top of my head, but we can audit all this). We can go audit them one by one and make a decision. But for stuff like use_debugfs() and kernel_supports(), we don't need atomic operations. It works and will work correctly for the purposes it's being used for, even if some portion of C standard (sorry, not an expert on standards) declares some of the aspects as undefined behavior. If compilers somehow exploit such UBs, they will break, say, Linux kernel (there are lots of not explicitly atomic operations done there, see READ_ONCE/WRITE_ONCE), and they will just undo such "optimizations". In any case, this is internal libbpf implementation details that won't lead to any crashes or misbehaviors, it's all "eventually consistent" caches. Worst case we'll repeat some feature detection on two parallel threads, which is not a bug or a problem. But this is all implementation details, and they are called from APIs that indeed are not thread-safe (as I mentioned, bpf_object manipulations are not, generally speaking, thread-safe). But also note that it's a bit more nuanced still, as, say, bpf_object__load() can be called by multiple parallel threads provided it's called for two independent instances of I agree we can document all that better on libbpf side, of course. As usual, patches are very welcome. |
Thanks Andrii. That confirms the mental model about Once that is done I believe we should be in agreement that there are no issues and that |
Yeah, sounds good to me. I would contribute but I've personally never contributed to anything in the Linux kernel tree (yet), so it might take a bit longer to get acquainted with the mailing list workflow. Feel free to do it if you want :) |
It may be a good chance to get acquainted with how things are done there, so if you have entertained the thought of contributing there, I'd be happy to leave it to you. From my perspective, we are not blocked on it, so take all the time you need. Just let me know how you decide. |
@danielocfb I have someone on Meta side interested in making relevant changes, so hopefully we'll get this fixed and better documented on libbpf side |
Thanks, seem the patch has landed upstream now. |
Unfortunately
set_print
cannot be safely wrapped as the underlying call is not thread safe, as documented in anotherlibbpf_
function hereAs such I think it needs to be marked
unsafe
for this library to be considered a safe wrapper aroundlibbpf
I also deleted the "Temporarily disable printing" example as it seem unlikely that usage pattern is realistic outside the most trivial of programs.