Skip to content

Commit c9afd72

Browse files
committed
Handle Task::dup_from case where other's [vdso] overlaps the current task's [vvar].
1 parent bc24929 commit c9afd72

File tree

1 file changed

+60
-37
lines changed

1 file changed

+60
-37
lines changed

src/Task.cc

Lines changed: 60 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3785,38 +3785,62 @@ static bool copy_mem_mapping_just_used(Task* from, Task* to, const KernelMapping
37853785
return true;
37863786
}
37873787

3788-
static void move_vdso_mapping(AutoRemoteSyscalls &remote, const KernelMapping &km) {
3789-
for (const auto& m : remote.task()->vm()->maps()) {
3790-
if (m.map.is_vdso() && m.map.start() != km.start()) {
3791-
size_t size = m.map.size();
3792-
ASSERT(remote.task(), size == km.size()) << "Inconsistent VDSO sizes";
3793-
// Handle case where old and new addresses overlap by finding a free range early in the
3794-
// address space we can use as a temporary buffer. VDSOs are always at fairly high
3795-
// addresses so this shouldn't introduce any new overlap issues.
3796-
remote_ptr<void> free_mem = remote.task()->vm()->find_free_memory(remote.task(), size,
3797-
remote_ptr<void>(65536), AddressSpace::FindFreeMemoryPolicy::STRICT_SEARCH);
3798-
if (!free_mem) {
3799-
FATAL() << "Can't find free memory for VDSO " << m.map;
3788+
static void move_vdso_and_vvar_mappings(AutoRemoteSyscalls& remote,
3789+
const KernelMapping& vdso, const KernelMapping& vvar) {
3790+
/* Remap VDSO and VVAR to the addresses is used in the target process,
3791+
before they get unmapped.
3792+
Otherwise the kernel seems to put the address of the original
3793+
VDSO __kernel_rt_sigreturn function as return address on the stack.
3794+
This might not affect x86_64 because there __restore_rt
3795+
located in libpthread.so.0 is used.
3796+
*/
3797+
3798+
// Handle case where old and new addresses overlap by finding a free range early in the
3799+
// address space we can use as a temporary buffer. VDSOs are always at fairly high
3800+
// addresses so this shouldn't introduce any new overlap issues.
3801+
// We move VDSO and VVAR to their temp addresses first, then move both of them to their
3802+
// final address, to avoid situations where current's VDSO overlaps target's VVAR or
3803+
// vice versa.
3804+
remote_ptr<void> vdso_temp_address = remote.task()->vm()->find_free_memory(remote.task(),
3805+
vdso.size() + vvar.size(),
3806+
remote_ptr<void>(65536), AddressSpace::FindFreeMemoryPolicy::STRICT_SEARCH);
3807+
remote_ptr<void> vvar_temp_address = vdso_temp_address + vdso.size();
3808+
3809+
for (int phase = 0; phase < 2; ++phase) {
3810+
for (const auto& m : remote.task()->vm()->maps()) {
3811+
const KernelMapping* move_to = nullptr;
3812+
remote_ptr<void> temp_address;
3813+
if (m.map.is_vdso() && vdso.size()) {
3814+
move_to = &vdso;
3815+
temp_address = vdso_temp_address;
3816+
} else if (m.map.is_vvar() && vvar.size()) {
3817+
move_to = &vvar;
3818+
temp_address = vvar_temp_address;
38003819
}
3801-
if (MemoryRange(free_mem, size).intersects(MemoryRange(m.map.start(), size))) {
3802-
FATAL() << "Free memory found overlaps current VDSO address";
3820+
if (!move_to || m.map.start() == move_to->start()) {
3821+
continue;
38033822
}
3804-
if (MemoryRange(free_mem, size).intersects(MemoryRange(km.start(), size))) {
3805-
FATAL() << "Free memory found overlaps new VDSO address";
3823+
3824+
size_t size = m.map.size();
3825+
ASSERT(remote.task(), size == move_to->size()) << "Inconsistent VDSO sizes";
3826+
ASSERT(remote.task(), !temp_address.is_null()) << "Can't find free memory for VDSO " << m.map;
3827+
ASSERT(remote.task(),
3828+
!MemoryRange(temp_address, size).intersects(MemoryRange(m.map.start(), size)))
3829+
<< "Free memory found overlaps current VDSO address";
3830+
ASSERT(remote.task(),
3831+
!MemoryRange(temp_address, size).intersects(MemoryRange(move_to->start(), size)))
3832+
<< "Free memory found overlaps new VDSO address";
3833+
3834+
if (phase == 0) {
3835+
LOG(debug) << "Moving VDSO for " << remote.task()->tid << " to temp " << temp_address;
3836+
remote.infallible_syscall(syscall_number_for_mremap(remote.arch()), m.map.start(), size,
3837+
size, MREMAP_MAYMOVE | MREMAP_FIXED, temp_address);
3838+
} else {
3839+
remote.infallible_syscall(syscall_number_for_mremap(remote.arch()), temp_address, size,
3840+
size, MREMAP_MAYMOVE | MREMAP_FIXED, move_to->start());
3841+
remote.task()->vm()->remap(remote.task(), m.map.start(), size, move_to->start(), size,
3842+
MREMAP_MAYMOVE | MREMAP_FIXED);
38063843
}
3807-
LOG(debug) << "Moving VDSO for " << remote.task()->tid;
3808-
/* Remap VDSO to the address that is used in the target process,
3809-
before it gets unmapped.
3810-
Otherwise the kernel seems to put the address of the original
3811-
VDSO __kernel_rt_sigreturn function as return address on the stack.
3812-
This might not affect x86_64 because there __restore_rt
3813-
located in libpthread.so.0 is used. */
3814-
remote.infallible_syscall(syscall_number_for_mremap(remote.arch()), m.map.start(), size,
3815-
size, MREMAP_MAYMOVE | MREMAP_FIXED, free_mem);
3816-
remote.infallible_syscall(syscall_number_for_mremap(remote.arch()), free_mem, size,
3817-
size, MREMAP_MAYMOVE | MREMAP_FIXED, km.start());
3818-
remote.task()->vm()->remap(remote.task(), m.map.start(), size, km.start(), size,
3819-
MREMAP_MAYMOVE | MREMAP_FIXED);
38203844
}
38213845
}
38223846
}
@@ -3833,7 +3857,7 @@ void Task::dup_from(Task *other) {
38333857
KernelMapping stack_mapping;
38343858
bool found_stack = false;
38353859
KernelMapping vdso_mapping;
3836-
bool found_vdso = false;
3860+
KernelMapping vvar_mapping;
38373861

38383862
for (auto map : other->vm()->maps()) {
38393863
auto km = map.map;
@@ -3854,10 +3878,11 @@ void Task::dup_from(Task *other) {
38543878
found_stack = true;
38553879
} else {
38563880
mappings.push_back(km);
3857-
}
3858-
if (km.is_vdso()) {
3859-
found_vdso = true;
3860-
vdso_mapping = km;
3881+
if (km.is_vdso()) {
3882+
vdso_mapping = km;
3883+
} else if (km.is_vvar()) {
3884+
vvar_mapping = km;
3885+
}
38613886
}
38623887
}
38633888
ASSERT(this, found_stack);
@@ -3869,9 +3894,7 @@ void Task::dup_from(Task *other) {
38693894
}
38703895
{
38713896
AutoRemoteSyscalls remote(this, AutoRemoteSyscalls::DISABLE_MEMORY_PARAMS);
3872-
if (found_vdso) {
3873-
move_vdso_mapping(remote, vdso_mapping);
3874-
}
3897+
move_vdso_and_vvar_mappings(remote, vdso_mapping, vvar_mapping);
38753898
LOG(debug) << "Unmapping memory for " << tid;
38763899
// TODO: Only do this if the rr page isn't already mapped
38773900
this->vm()->unmap_all_but_rr_page(remote);

0 commit comments

Comments
 (0)