Skip to content
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

Potential to modify ordering for compare_exhange in imp_std module #252

Closed
wang384670111 opened this issue Dec 28, 2023 · 5 comments
Closed

Comments

@wang384670111
Copy link

wang384670111 commented Dec 28, 2023

I developed a static analysis tool to detect issues related to memory ordering, thread performance, and security. During my examination of several crates, I encountered alarms triggered by the following code:

once_cell/src/imp_std.rs

Lines 213 to 232 in c48d3c2

let node = Waiter {
thread: Cell::new(Some(thread::current())),
signaled: AtomicBool::new(false),
next: strict::map_addr(curr_queue, |q| q & !STATE_MASK),
};
let me = &node as *const Waiter as *mut Waiter;
let exchange = queue.compare_exchange(
curr_queue,
strict::map_addr(me, |q| q | curr_state),
Ordering::Release,
Ordering::Relaxed,
);
if let Err(new_queue) = exchange {
if strict::addr(new_queue) & STATE_MASK != curr_state {
return;
}
curr_queue = new_queue;
continue;
}

The meaning of the code should be that we need to have a successful initialization operation in Line222, but the compare exchange also entails reading the atomic pointer to the exchange. Therefore, it's necessary to use AcqRel and Acquire to observe any other modifications to the memory that the atomic pointer references, both when the compare exchange succeeds and when it fails.

I think compare exchange for queue uses AcqRel for the success case and Acquire for the fail case.

(happy to make a PR if this looks reasonable)

@matklad
Copy link
Owner

matklad commented Dec 28, 2023

Oh dear, that’s big if true, thanks! I think the same code has been running in std for the past four years?

It does look reasonable on the first glance: that compare exchange is an rmw (read-modify-write) operation, and, if we care to Relase w, we will probably want to Acquire r…

The linage of this ordering traces back to rust-lang/rust#65719, where SeqCst orderings were first relaxed.

Will take a close look once I properly wake-up 🤣

@matklad
Copy link
Owner

matklad commented Dec 28, 2023

Hm, having properly woken up, I think the code is correct. The reason for that is the call site of this function:

once_cell/src/imp_std.rs

Lines 202 to 203 in c48d3c2

wait(queue, curr_queue);
curr_queue = queue.load(Ordering::Acquire);

We actually don't need wait to have Acquire semantics, because we are going to just re-load with Acquire anyway?

@wang384670111
Copy link
Author

wang384670111 commented Dec 28, 2023

It appears to be a false positive from my detector. Upon closer examination of the code, I realized that there is no dereferencing operation involved in the exchange, the strict::addr(new_queue) operates on the location of the pointer itself, rather than the address to which the pointer points, so using Relaxed here is acceptable. However, if dereferencing of the exchange is involved, Acquire should be used in this scenario.

once_cell/src/imp_std.rs

Lines 226 to 232 in c48d3c2

if let Err(new_queue) = exchange {
if strict::addr(new_queue) & STATE_MASK != curr_state {
return;
}
curr_queue = new_queue;
continue;
}

It seems that I have to modify the detection pattern😂.

@wang384670111
Copy link
Author

Hm, having properly woken up, I think the code is correct. The reason for that is the call site of this function:

once_cell/src/imp_std.rs

Lines 202 to 203 in c48d3c2

wait(queue, curr_queue);
curr_queue = queue.load(Ordering::Acquire);

We actually don't need wait to have Acquire semantics, because we are going to just re-load with Acquire anyway?

I agree, Acquire is fine here.

@wang384670111
Copy link
Author

After lying in bed for a while, I feel much more awake now! The ordering in the code is fine, and I've figured out how to modify the detector pattern. I think we can consider this issue closed. Good night! 😴

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants