This is the mail archive of the systemtap@sourceware.org mailing list for the systemtap project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Bug uprobes/5274] New: uprobes: Handle longjmps


As documented in Documentation/uprobes.txt, a longjmp in a probed program may
cause one or more uretprobed functions to terminate without returning.  This
would leave uretprobe_instances on the task's list, with the result that (a)
bypassed uretprobe_instances accumulate without being freed, or (b) a bypassed
uretprobe_instance is mistaken for one further up the call chain -- resulting in
a return to the wrong address.

I think the following solution would work:

1. Add a stack_ptr member to struct uretprobe_instance.  (That's what the
reserved1 member is for, actually.)  When a uretprobed function is called, an
architecture-specific function is passed the regs pointer and asked to predict
the value of the stack pointer when that function returns.  For powerpc, I think
it'd be the same as the current value.  i386 or x86_64 would have to add 4 or 8
bytes to account for the return address being popped on return.

2. When a uretprobed function is called or returns, any uretprobe_instances with
stack_ptr beyond the current top of stack are presumed to have been previously
bypassed; they are recycled without calling their handlers.

3. Similarly, when a uretprobe is unregistered, each thread's
uretprobe_instances are examined, and any beyond the current TOS are recycled. 
I think zap_uretprobe_instances() can do this safely, because it holds the uproc
write-locked, and no thread will fuss its uretprobe_instance list without
read-locking the uproc.

I have this mostly coded.

The x86 instruction set allows instructions like "ret $8", which means "pop the
return address, then pop 8 more bytes, then jump to the popped RA."  Such
instructions would defeat this approach**, but such instructions also break the
ABI.  [**unless we required the caller of register_uretprobe() to specify the
amount of "extra pop" in such instances]

BTW, on a related topic...  For code like
	extern void f1(void);
	void f2(void) { f1(); }
the code generated for f2() may end with a JUMP to f1 rather than a call and
return.  f1() returns to f2()'s return address.  I think we're OK here even if
both f1() and f2() are uretprobed.  When f2() is called, its return address will
be saved in a uretprobe_instance, and the uretprobe_trampoline address will
replace the return address on the stack.  When f1() is entered, its
uretprobe_instance will get the uretprobe_trampoline address as its return
address... giving us the same situation we see when we have multiple uretprobes
associated with the same function.

-- 
           Summary: uprobes: Handle longjmps
           Product: systemtap
           Version: unspecified
            Status: NEW
          Severity: normal
          Priority: P2
         Component: uprobes
        AssignedTo: systemtap at sources dot redhat dot com
        ReportedBy: jkenisto at us dot ibm dot com


http://sourceware.org/bugzilla/show_bug.cgi?id=5274

------- You are receiving this mail because: -------
You are the assignee for the bug, or are watching the assignee.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]