All of lore.kernel.org
 help / color / mirror / Atom feed
* uprobes misses breakpoint insertion into VM_WRITE mappings
@ 2018-03-15 20:48 Mathieu Desnoyers
  2018-03-16 16:52 ` Oleg Nesterov
  0 siblings, 1 reply; 3+ messages in thread
From: Mathieu Desnoyers @ 2018-03-15 20:48 UTC (permalink / raw)
  To: Oleg Nesterov, Erica Bugden
  Cc: Srikar Dronamraju, rostedt, Ingo Molnar, Peter Zijlstra,
	Thomas Gleixner, linux-kernel

Hi,

Erica has been working on extending test-cases for uprobes, and found
something unexpected:

Since commit e40cfce626a5 "uprobes: Restrict valid_vma(false) to skip VM_SHARED vmas"
uprobes does not insert breakpoints into mappings mprotect'd as writeable.

This issue can be reproduced by compiling a library without PIC (not using GOT),
and then concurrently:

A) Load the library (dynamic loader mprotect the code as writeable to do
   the relocations, and then mprotect as executable),

B) Enable a uprobe through perf.

(it is a race window between the two mprotect syscalls)

It appears that the following restriction in valid_vma() is responsible
for this behavior:

        if (is_register)
                flags |= VM_WRITE;

I don't figure a clear explanation for this flag based on the function
comment nor the commit changelog. Any idea on whether this is really
needed ?

Note that on uprobes unregister, it allows removing a breakpoint event
on a writeable mapping, so there is clearly a discrepancy between the
level of paranoia associated with registration and unregistration.

Thanks,

Mathieu

-- 
Mathieu Desnoyers
EfficiOS Inc.
http://www.efficios.com

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: uprobes misses breakpoint insertion into VM_WRITE mappings
  2018-03-15 20:48 uprobes misses breakpoint insertion into VM_WRITE mappings Mathieu Desnoyers
@ 2018-03-16 16:52 ` Oleg Nesterov
  2018-03-22 21:48   ` Mathieu Desnoyers
  0 siblings, 1 reply; 3+ messages in thread
From: Oleg Nesterov @ 2018-03-16 16:52 UTC (permalink / raw)
  To: Mathieu Desnoyers
  Cc: Erica Bugden, Srikar Dronamraju, rostedt, Ingo Molnar,
	Peter Zijlstra, Thomas Gleixner, linux-kernel

On 03/15, Mathieu Desnoyers wrote:
>
> Hi,
>
> Erica has been working on extending test-cases for uprobes, and found
> something unexpected:
>
> Since commit e40cfce626a5 "uprobes: Restrict valid_vma(false) to skip VM_SHARED vmas"
> uprobes does not insert breakpoints into mappings mprotect'd as writeable.

Not really, VM_WRITE was illegal from the very beginning, this commit only
affects the "is_register == false" case.

> This issue can be reproduced by compiling a library without PIC (not using GOT),
> and then concurrently:
>
> A) Load the library (dynamic loader mprotect the code as writeable to do
>    the relocations, and then mprotect as executable),
>
> B) Enable a uprobe through perf.
>
> (it is a race window between the two mprotect syscalls)
>
> It appears that the following restriction in valid_vma() is responsible
> for this behavior:
>
>         if (is_register)
>                 flags |= VM_WRITE;
>
> I don't figure a clear explanation for this flag based on the function
> comment nor the commit changelog. Any idea on whether this is really
> needed ?

Because we do not want to modify the writable area. If nothing else, this
can break the application which writes to the page we are going to replace.

> Note that on uprobes unregister, it allows removing a breakpoint event
> on a writeable mapping,

Yes. Because a probed apllication can do mprotect() after the kernel installs
the breakpoint. And we have to remove this breakpoint in any case, even if
this is unsafe too.

Oleg.

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: uprobes misses breakpoint insertion into VM_WRITE mappings
  2018-03-16 16:52 ` Oleg Nesterov
@ 2018-03-22 21:48   ` Mathieu Desnoyers
  0 siblings, 0 replies; 3+ messages in thread
From: Mathieu Desnoyers @ 2018-03-22 21:48 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Erica Bugden, Srikar Dronamraju, rostedt, Ingo Molnar,
	Peter Zijlstra, Thomas Gleixner, linux-kernel

----- On Mar 16, 2018, at 12:52 PM, Oleg Nesterov oleg@redhat.com wrote:

> On 03/15, Mathieu Desnoyers wrote:
>>
>> Hi,
>>
>> Erica has been working on extending test-cases for uprobes, and found
>> something unexpected:
>>
>> Since commit e40cfce626a5 "uprobes: Restrict valid_vma(false) to skip VM_SHARED
>> vmas"
>> uprobes does not insert breakpoints into mappings mprotect'd as writeable.
> 
> Not really, VM_WRITE was illegal from the very beginning, this commit only
> affects the "is_register == false" case.

Good point. I only noticed it after further archeology research through git blame. ;)

> 
>> This issue can be reproduced by compiling a library without PIC (not using GOT),
>> and then concurrently:
>>
>> A) Load the library (dynamic loader mprotect the code as writeable to do
>>    the relocations, and then mprotect as executable),
>>
>> B) Enable a uprobe through perf.
>>
>> (it is a race window between the two mprotect syscalls)
>>
>> It appears that the following restriction in valid_vma() is responsible
>> for this behavior:
>>
>>         if (is_register)
>>                 flags |= VM_WRITE;
>>
>> I don't figure a clear explanation for this flag based on the function
>> comment nor the commit changelog. Any idea on whether this is really
>> needed ?
> 
> Because we do not want to modify the writable area. If nothing else, this
> can break the application which writes to the page we are going to replace.

I fully agree on never poking writeable pages. However, I think something is
missing here.

The plausible scenario that's inherently racy is:

1) dynamic loader mprotect(write) the mapping,
2) dynamic loader performs relocations
3) (concurrently) uprobe is enabled for this mapping
4) dynamic loader mprotect(read|exec) the mapping.

Again, I fully agree that step (3) should *not* touch the mapping while it is
writeable, because there is no way to synchronize the the application which is
expecting to be sole owner of the code being modified.

However, what I think is missing here is that step (4) (mprotect(read|exec))
should apply all "pending" modifications for that mapping.

This means that if some code is always writeable, uprobes will not
mess with it. However, if the pattern is to make it writeable for a short
while and then make it read|exec again, then the queued modifications
could be applied by mprotect().

What is not entirely clear to me is how to bound the number of pending
modifications and where to these pending uprobes operation queues should
reside.

Thoughts ?

Thanks,

Mathieu


> 
>> Note that on uprobes unregister, it allows removing a breakpoint event
>> on a writeable mapping,
> 
> Yes. Because a probed apllication can do mprotect() after the kernel installs
> the breakpoint. And we have to remove this breakpoint in any case, even if
> this is unsafe too.
> 
> Oleg.

-- 
Mathieu Desnoyers
EfficiOS Inc.
http://www.efficios.com

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2018-03-22 21:48 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-03-15 20:48 uprobes misses breakpoint insertion into VM_WRITE mappings Mathieu Desnoyers
2018-03-16 16:52 ` Oleg Nesterov
2018-03-22 21:48   ` Mathieu Desnoyers

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.