-
Notifications
You must be signed in to change notification settings - Fork 566
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
Add interoperability with W^X policies #3556
Comments
I am observing an interesting performance issue: I have to use mmap to incrementally commit instead of mprotect, since most W^X policies do not like mprotect. With mmap, I see a huge (4-5 second!) delay at init time apparently due to the kernel IMA (Integrity Measurement Architecture) going and computing sha256 digests of every page in our anonymous mmap. Doing this for an anonymous mmap seems bizarre. It happens for both memfd_create and /dev/shm as the backing file. We can see the delay scaling by vm size:
Note how it's all kernel time. perf shows things like:
On kernels without IMA enabled the delay is not there. |
A simple reproducer shows that the hash is only performed on the first mmap +x of any size, and covers the full file size. Subsequent mmaps do not incur extra overhead. So this is limited to DR init and will not show up later during app execution.
|
I messed the number up in my commits so manually adding references here:
|
I failed to think about how to handle fork. We have to use MAP_SHARED to mirror the views, so we will not get copy-on-write (even if we did we wouldn't get mirroring in a copy). I think the child has to make a new memfd file and copy all the old content into new views? For a large vmcode that could take a while. Fortunately most forks are done early in a process and rarely late in a large (esp multi-threaded) app? |
We also have trouble working with dr_nonheap_alloc() with +wx memory. Since I don't think we can turn this on by default yet I'm going to just issue a usage error in that case. |
The fork copy solution of course has a race. I'm not sure there's a better solution. For now I may live with the race as the alternative is having the parent wait. Summarizing where we are:
|
Adds W^X-aware handling of several generated code cases missed in the original implementation: + mangle_syscall_code() + shift_ctis_in_fragment() Adds best-effort handling of fork (there is still a race, and potentially noticeable overhead). Adds a fix for the proper heap type when extending reachable heap units (which showed up as a trace encoding bug). Adds a usage error when using dr_nonheap_alloc() with +wx memory, which we do not support with W^X. Changes the single -satisfy_w_xor_x test into a cross-cutting option set, expanding into multiple tests to cover more behavior. To include drcachesim tests here, a new test feature _self_serial is added which adds dependences for copies of the same test run under different options. Issue: #3556
Adds W^X-aware handling of several generated code cases missed in the original implementation: + mangle_syscall_code() + shift_ctis_in_fragment() Adds best-effort handling of fork (there is still a race, and potentially noticeable overhead). Adds a fix for the proper heap type when extending reachable heap units (which showed up as a trace encoding bug). Adds a usage error when using dr_nonheap_alloc() with +wx memory, which we do not support with W^X. Changes the single -satisfy_w_xor_x test into a cross-cutting option set, expanding into multiple tests to cover more behavior. To include drcachesim tests here, a new test feature _self_serial is added which adds dependences for copies of the same test run under different options. Issue: #3556
I see two solutions to the fork race:
I'm not sure which is better. |
Changes how fork is handled to avoid races. Now the parent makes an anonymous private temporary copy of vmcode pre-fork, freeing it post-fork. The child sets up its new mappings from the temporary copy, avoiding any issues with copying the parent's live data. Adds a sanity fork regression test. Issue: #3556
Changes how fork is handled to avoid races. Now the parent makes an anonymous private temporary copy of vmcode pre-fork, freeing it post-fork. The child sets up its new mappings from the temporary copy, avoiding any issues with copying the parent's live data. Adds a sanity fork regression test. Issue: #3556
DR_ALLOC_CACHE_REACHABLE is in the same boat as dr_nonheap_alloc: we'd have to expose the mapping function to support it. It should also have an assert for now. |
Just clarifying one reason this is left open: how to enable clients to operate properly wrt W^X. Better would be to shrink the vmcode mapping and have a 2nd one there for |
This is a feature request to add support in DR for working in the presence of W^X policies. These PaX-like policies typically disallow execution from any page that came from mmap +wx or ever had mprotect +x applied to it, which rules out writing and later marking read-only. The proposal here is to use a dual-mapping strategy: having two virtual pages backed by the same physical page, with one +rw for writing and the other +rx for execution.
This will likely be limited to 64-bit Linux but could be extended to Windows in the future if desired.
With #1132 in place, we can make a new reservation the same size as vmcode and have a constant offset between each pair of pages. We would then keep all variables tracking addresses as scalars, and when we want to write, we would simply add the offset.
It may be best to first extend #774 and #1132 to make the size of vmcode 2G by default, to avoid any issues with out-of-reservation allocations.
The text was updated successfully, but these errors were encountered: