2017-03-22 20:00:35 +03:00
/*
* Copyright ( c ) 2017 Facebook
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation .
*/
# define KBUILD_MODNAME "foo"
# include <linux/ptrace.h>
# include <linux/version.h>
# include <uapi/linux/bpf.h>
# include <uapi/linux/in6.h>
2020-01-20 16:06:49 +03:00
# include <bpf/bpf_helpers.h>
# include <bpf/bpf_tracing.h>
samples: bpf: Fix bpf programs with kprobe/sys_connect event
Currently, BPF programs with kprobe/sys_connect does not work properly.
Commit 34745aed515c ("samples/bpf: fix kprobe attachment issue on x64")
This commit modifies the bpf_load behavior of kprobe events in the x64
architecture. If the current kprobe event target starts with "sys_*",
add the prefix "__x64_" to the front of the event.
Appending "__x64_" prefix with kprobe/sys_* event was appropriate as a
solution to most of the problems caused by the commit below.
commit d5a00528b58c ("syscalls/core, syscalls/x86: Rename struct
pt_regs-based sys_*() to __x64_sys_*()")
However, there is a problem with the sys_connect kprobe event that does
not work properly. For __sys_connect event, parameters can be fetched
normally, but for __x64_sys_connect, parameters cannot be fetched.
ffffffff818d3520 <__x64_sys_connect>:
ffffffff818d3520: e8 fb df 32 00 callq 0xffffffff81c01520
<__fentry__>
ffffffff818d3525: 48 8b 57 60 movq 96(%rdi), %rdx
ffffffff818d3529: 48 8b 77 68 movq 104(%rdi), %rsi
ffffffff818d352d: 48 8b 7f 70 movq 112(%rdi), %rdi
ffffffff818d3531: e8 1a ff ff ff callq 0xffffffff818d3450
<__sys_connect>
ffffffff818d3536: 48 98 cltq
ffffffff818d3538: c3 retq
ffffffff818d3539: 0f 1f 80 00 00 00 00 nopl (%rax)
As the assembly code for __x64_sys_connect shows, parameters should be
fetched and set into rdi, rsi, rdx registers prior to calling
__sys_connect.
Because of this problem, this commit fixes the sys_connect event by
first getting the value of the rdi register and then the value of the
rdi, rsi, and rdx register through an offset based on that value.
Fixes: 34745aed515c ("samples/bpf: fix kprobe attachment issue on x64")
Signed-off-by: Daniel T. Lee <danieltimlee@gmail.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Andrii Nakryiko <andriin@fb.com>
Link: https://lore.kernel.org/bpf/20200707184855.30968-2-danieltimlee@gmail.com
2020-07-07 21:48:52 +03:00
# include <bpf/bpf_core_read.h>
# include "trace_common.h"
2017-03-22 20:00:35 +03:00
# define MAX_NR_PORTS 65536
/* map #0 */
2020-07-07 21:48:53 +03:00
struct inner_a {
__uint ( type , BPF_MAP_TYPE_ARRAY ) ;
__type ( key , u32 ) ;
__type ( value , int ) ;
__uint ( max_entries , MAX_NR_PORTS ) ;
} port_a SEC ( " .maps " ) ;
2017-03-22 20:00:35 +03:00
/* map #1 */
2020-07-07 21:48:53 +03:00
struct inner_h {
__uint ( type , BPF_MAP_TYPE_HASH ) ;
__type ( key , u32 ) ;
__type ( value , int ) ;
__uint ( max_entries , 1 ) ;
} port_h SEC ( " .maps " ) ;
2017-03-22 20:00:35 +03:00
/* map #2 */
2020-07-07 21:48:53 +03:00
struct {
__uint ( type , BPF_MAP_TYPE_HASH ) ;
__type ( key , u32 ) ;
__type ( value , int ) ;
__uint ( max_entries , 1 ) ;
} reg_result_h SEC ( " .maps " ) ;
2017-03-22 20:00:35 +03:00
/* map #3 */
2020-07-07 21:48:53 +03:00
struct {
__uint ( type , BPF_MAP_TYPE_HASH ) ;
__type ( key , u32 ) ;
__type ( value , int ) ;
__uint ( max_entries , 1 ) ;
} inline_result_h SEC ( " .maps " ) ;
2017-03-22 20:00:35 +03:00
/* map #4 */ /* Test case #0 */
2020-07-07 21:48:53 +03:00
struct {
__uint ( type , BPF_MAP_TYPE_ARRAY_OF_MAPS ) ;
__uint ( max_entries , MAX_NR_PORTS ) ;
__uint ( key_size , sizeof ( u32 ) ) ;
__array ( values , struct inner_a ) ; /* use inner_a as inner map */
} a_of_port_a SEC ( " .maps " ) ;
2017-03-22 20:00:35 +03:00
/* map #5 */ /* Test case #1 */
2020-07-07 21:48:53 +03:00
struct {
__uint ( type , BPF_MAP_TYPE_HASH_OF_MAPS ) ;
__uint ( max_entries , 1 ) ;
__uint ( key_size , sizeof ( u32 ) ) ;
__array ( values , struct inner_a ) ; /* use inner_a as inner map */
} h_of_port_a SEC ( " .maps " ) ;
2017-03-22 20:00:35 +03:00
/* map #6 */ /* Test case #2 */
2020-07-07 21:48:53 +03:00
struct {
__uint ( type , BPF_MAP_TYPE_HASH_OF_MAPS ) ;
__uint ( max_entries , 1 ) ;
__uint ( key_size , sizeof ( u32 ) ) ;
__array ( values , struct inner_h ) ; /* use inner_h as inner map */
} h_of_port_h SEC ( " .maps " ) ;
2017-03-22 20:00:35 +03:00
static __always_inline int do_reg_lookup ( void * inner_map , u32 port )
{
int * result ;
result = bpf_map_lookup_elem ( inner_map , & port ) ;
return result ? * result : - ENOENT ;
}
static __always_inline int do_inline_array_lookup ( void * inner_map , u32 port )
{
int * result ;
if ( inner_map ! = & port_a )
return - EINVAL ;
result = bpf_map_lookup_elem ( & port_a , & port ) ;
return result ? * result : - ENOENT ;
}
static __always_inline int do_inline_hash_lookup ( void * inner_map , u32 port )
{
int * result ;
if ( inner_map ! = & port_h )
return - EINVAL ;
result = bpf_map_lookup_elem ( & port_h , & port ) ;
return result ? * result : - ENOENT ;
}
2020-09-15 14:55:19 +03:00
SEC ( " kprobe/__sys_connect " )
2017-03-22 20:00:35 +03:00
int trace_sys_connect ( struct pt_regs * ctx )
{
struct sockaddr_in6 * in6 ;
u16 test_case , port , dst6 [ 8 ] ;
int addrlen , ret , inline_ret , ret_key = 0 ;
u32 port_key ;
void * outer_map , * inner_map ;
bool inline_hash = false ;
2020-09-15 14:55:19 +03:00
in6 = ( struct sockaddr_in6 * ) PT_REGS_PARM2_CORE ( ctx ) ;
addrlen = ( int ) PT_REGS_PARM3_CORE ( ctx ) ;
2017-03-22 20:00:35 +03:00
if ( addrlen ! = sizeof ( * in6 ) )
return 0 ;
2019-11-02 02:18:01 +03:00
ret = bpf_probe_read_user ( dst6 , sizeof ( dst6 ) , & in6 - > sin6_addr ) ;
2017-03-22 20:00:35 +03:00
if ( ret ) {
inline_ret = ret ;
goto done ;
}
if ( dst6 [ 0 ] ! = 0xdead | | dst6 [ 1 ] ! = 0xbeef )
return 0 ;
test_case = dst6 [ 7 ] ;
2019-11-02 02:18:01 +03:00
ret = bpf_probe_read_user ( & port , sizeof ( port ) , & in6 - > sin6_port ) ;
2017-03-22 20:00:35 +03:00
if ( ret ) {
inline_ret = ret ;
goto done ;
}
port_key = port ;
ret = - ENOENT ;
if ( test_case = = 0 ) {
outer_map = & a_of_port_a ;
} else if ( test_case = = 1 ) {
outer_map = & h_of_port_a ;
} else if ( test_case = = 2 ) {
outer_map = & h_of_port_h ;
} else {
ret = __LINE__ ;
inline_ret = ret ;
goto done ;
}
inner_map = bpf_map_lookup_elem ( outer_map , & port_key ) ;
if ( ! inner_map ) {
ret = __LINE__ ;
inline_ret = ret ;
goto done ;
}
ret = do_reg_lookup ( inner_map , port_key ) ;
if ( test_case = = 0 | | test_case = = 1 )
inline_ret = do_inline_array_lookup ( inner_map , port_key ) ;
else
inline_ret = do_inline_hash_lookup ( inner_map , port_key ) ;
done :
bpf_map_update_elem ( & reg_result_h , & ret_key , & ret , BPF_ANY ) ;
bpf_map_update_elem ( & inline_result_h , & ret_key , & inline_ret , BPF_ANY ) ;
return 0 ;
}
char _license [ ] SEC ( " license " ) = " GPL " ;
u32 _version SEC ( " version " ) = LINUX_VERSION_CODE ;