Workaround for Non-Functioning uretprobes with Oracle Database Software

uprobe

uprobes don’t work correctly with Oracle database software, for example:

bpftrace -e 'uprobe:/u00/oracle/orabase/product/21.6.0.0.220419_a/bin/oracle:kstmgetsectick
{
  printf("HERE\n") ;
}'

The uprobe on the function entry kstmgetsectick, one of the Oracle C functions, cannot be attached:

Attaching 1 probe...
cannot attach uprobe, Invalid argument
ERROR: Error attaching probe: uprobe:/u00/oracle/orabase/product/21.6.0.0.220419_a/bin/oracle:kstmgetsectick

chris_skyflier explained that uprobes can’t deal with the byte sequence “0x66 0x90”, which Oracle inserted in each function’s prologue to facilitate hot patching. In other words, the Oracle patching feature broke uprobes.

Hatem Mahmoud explained the workaround – adding an offset to a uprobe to skip the problematic byte sequence:

bpftrace -e 'uprobe:/u00/oracle/orabase/product/21.6.0.0.220419_a/bin/oracle:kstmgetsectick+2
{
  printf("HERE\n") ;
}'

Attaching 1 probe...
HERE

uretprobe

uretprobe, the probe that fires on the function exit, doesn’t work for the same reason:

bpftrace -e 'uretprobe:/u00/oracle/orabase/product/21.6.0.0.220419_a/bin/oracle:kstmgetsectick
{
  printf("HERE\n") ;
}'
Attaching 1 probe...
cannot attach uprobe, Invalid argument
ERROR: Error attaching probe: uretprobe:/u00/oracle/orabase/product/21.6.0.0.220419_a/bin/oracle:kstmgetsectick

Unluckily, the workaround with offset doesn’t function either because uretprobes don’t support offsets.

bpftrace -e 'uretprobe:/u00/oracle/orabase/product/21.6.0.0.220419_a/bin/oracle:kstmgetsectick+2
{
  printf("HERE\n") ;
}'

stdin:1:1-84: ERROR: Offset not allowed
uretprobe:/u00/oracle/orabase/product/21.6.0.0.220419_a/bin/oracle:kstmgetsectick+2

What other workaround can we use?

Workaround

We can configure uprobe on the program address where the function returns. But, first, we have to identify the address of the ret instruction (or its cousin retq). Since we have to disassemble the function, and the licence agreement with Oracle prohibits that, I’m going to demonstrate this technique on the following simple C program:

int main( int argc, char *argv[] ){
    return 10 ;
}
cc ret.c -o ret
gdb ret <<< "disas main"
0x0000000000400536 <+0>:     push   %rbp
0x0000000000400537 <+1>:     mov    %rsp,%rbp
0x000000000040053a <+4>:     mov    %edi,-0x4(%rbp)
0x000000000040053d <+7>:     mov    %rsi,-0x10(%rbp)
0x0000000000400541 <+11>:    mov    $0xa,%eax
0x0000000000400546 <+16>:    pop    %rbp
0x0000000000400547 <+17>:    retq

The retq instruction is at the address 0x0000000000400547, which is at the offset +17. We can set uprobe with either the address or the offset:

bpftrace -e 'uprobe:./ret:main+17
{
  printf("%d\n", reg("ax")) ;
}'
bpftrace -e 'uprobe:./ret:0x0000000000400547
{
  printf("%d\n", reg("ax")) ;
}'

Both variants produce the same result.
The probe prints the function’s return value, which, according to the x86 calling convention, is stored in the eax CPU register.

Attaching 1 probe...
10

Summary

In summary, uprobe and uretprobe don’t work with Oracle database software. The workaround for uprobe is adding an offset to the function to skip the byte sequence that Oracle inserted into a prologue of each function. Unfortunately, this doesn’t work for uretprobes because they don’t allow offsets. Alternatively, you can emulate uretprobe with an uprobe having an offset on the function call return(s). The return value can be extracted from the eax CPU register. To determine the address/offset of the ret* calls, you’d need to disassemble the traced function. Keep in mind that Oracle Corp generally doesn’t allow disassembling.

Thanks for sharing

Nenad Noveljic

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.