2020-09-28 12:31:06 +01:00
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2020, Oracle and/or its affiliates. */
2020-09-29 04:32:31 -07:00
# include "btf_ptr.h"
2020-09-28 12:31:06 +01:00
# include <bpf/bpf_helpers.h>
# include <bpf/bpf_tracing.h>
# include <bpf/bpf_core_read.h>
# include <errno.h>
long ret = 0 ;
int num_subtests = 0 ;
int ran_subtests = 0 ;
bool skip = false ;
# define STRSIZE 2048
# define EXPECTED_STRSIZE 256
2021-02-27 06:17:26 +01:00
# if defined(bpf_target_s390)
/* NULL points to a readable struct lowcore on s390, so take the last page */
# define BADPTR ((void *)0xFFFFFFFFFFFFF000ULL)
# else
# define BADPTR 0
# endif
2020-09-28 12:31:06 +01:00
# ifndef ARRAY_SIZE
# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
# endif
struct {
__uint ( type , BPF_MAP_TYPE_PERCPU_ARRAY ) ;
__uint ( max_entries , 1 ) ;
__type ( key , __u32 ) ;
__type ( value , char [ STRSIZE ] ) ;
} strdata SEC ( " .maps " ) ;
static int __strncmp ( const void * m1 , const void * m2 , size_t len )
{
const unsigned char * s1 = m1 ;
const unsigned char * s2 = m2 ;
int i , delta = 0 ;
for ( i = 0 ; i < len ; i + + ) {
delta = s1 [ i ] - s2 [ i ] ;
if ( delta | | s1 [ i ] = = 0 | | s2 [ i ] = = 0 )
break ;
}
return delta ;
}
# if __has_builtin(__builtin_btf_type_id)
# define TEST_BTF(_str, _type, _flags, _expected, ...) \
do { \
static const char _expectedval [ EXPECTED_STRSIZE ] = \
_expected ; \
static const char _ptrtype [ 64 ] = # _type ; \
__u64 _hflags = _flags | BTF_F_COMPACT ; \
static _type _ptrdata = __VA_ARGS__ ; \
static struct btf_ptr _ptr = { } ; \
int _cmp ; \
\
+ + num_subtests ; \
if ( ret < 0 ) \
break ; \
+ + ran_subtests ; \
_ptr . ptr = & _ptrdata ; \
_ptr . type_id = bpf_core_type_id_kernel ( _type ) ; \
if ( _ptr . type_id < = 0 ) { \
ret = - EINVAL ; \
break ; \
} \
ret = bpf_snprintf_btf ( _str , STRSIZE , \
& _ptr , sizeof ( _ptr ) , _hflags ) ; \
if ( ret ) \
break ; \
_cmp = __strncmp ( _str , _expectedval , EXPECTED_STRSIZE ) ; \
if ( _cmp ! = 0 ) { \
bpf_printk ( " (%d) got %s " , _cmp , _str ) ; \
bpf_printk ( " (%d) expected %s " , _cmp , \
_expectedval ) ; \
ret = - EBADMSG ; \
break ; \
} \
} while ( 0 )
# endif
/* Use where expected data string matches its stringified declaration */
# define TEST_BTF_C(_str, _type, _flags, ...) \
TEST_BTF ( _str , _type , _flags , " ( " # _type " ) " # __VA_ARGS__ , \
__VA_ARGS__ )
/* TRACE_EVENT(netif_receive_skb,
* TP_PROTO ( struct sk_buff * skb ) ,
*/
SEC ( " tp_btf/netif_receive_skb " )
int BPF_PROG ( trace_netif_receive_skb , struct sk_buff * skb )
{
static __u64 flags [ ] = { 0 , BTF_F_COMPACT , BTF_F_ZERO , BTF_F_PTR_RAW ,
BTF_F_NONAME , BTF_F_COMPACT | BTF_F_ZERO |
BTF_F_PTR_RAW | BTF_F_NONAME } ;
static struct btf_ptr p = { } ;
__u32 key = 0 ;
int i , __ret ;
char * str ;
# if __has_builtin(__builtin_btf_type_id)
str = bpf_map_lookup_elem ( & strdata , & key ) ;
if ( ! str )
return 0 ;
/* Ensure we can write skb string representation */
p . type_id = bpf_core_type_id_kernel ( struct sk_buff ) ;
p . ptr = skb ;
for ( i = 0 ; i < ARRAY_SIZE ( flags ) ; i + + ) {
+ + num_subtests ;
ret = bpf_snprintf_btf ( str , STRSIZE , & p , sizeof ( p ) , 0 ) ;
if ( ret < 0 )
bpf_printk ( " returned %d when writing skb " , ret ) ;
+ + ran_subtests ;
}
/* Check invalid ptr value */
2021-02-27 06:17:26 +01:00
p . ptr = BADPTR ;
2020-09-28 12:31:06 +01:00
__ret = bpf_snprintf_btf ( str , STRSIZE , & p , sizeof ( p ) , 0 ) ;
if ( __ret > = 0 ) {
2021-02-27 06:17:26 +01:00
bpf_printk ( " printing %llx should generate error, got (%d) " ,
( unsigned long long ) BADPTR , __ret ) ;
2020-09-28 12:31:06 +01:00
ret = - ERANGE ;
}
/* Verify type display for various types. */
/* simple int */
TEST_BTF_C ( str , int , 0 , 1234 ) ;
TEST_BTF ( str , int , BTF_F_NONAME , " 1234 " , 1234 ) ;
/* zero value should be printed at toplevel */
TEST_BTF ( str , int , 0 , " (int)0 " , 0 ) ;
TEST_BTF ( str , int , BTF_F_NONAME , " 0 " , 0 ) ;
TEST_BTF ( str , int , BTF_F_ZERO , " (int)0 " , 0 ) ;
TEST_BTF ( str , int , BTF_F_NONAME | BTF_F_ZERO , " 0 " , 0 ) ;
TEST_BTF_C ( str , int , 0 , - 4567 ) ;
TEST_BTF ( str , int , BTF_F_NONAME , " -4567 " , - 4567 ) ;
/* simple char */
TEST_BTF_C ( str , char , 0 , 100 ) ;
TEST_BTF ( str , char , BTF_F_NONAME , " 100 " , 100 ) ;
/* zero value should be printed at toplevel */
TEST_BTF ( str , char , 0 , " (char)0 " , 0 ) ;
TEST_BTF ( str , char , BTF_F_NONAME , " 0 " , 0 ) ;
TEST_BTF ( str , char , BTF_F_ZERO , " (char)0 " , 0 ) ;
TEST_BTF ( str , char , BTF_F_NONAME | BTF_F_ZERO , " 0 " , 0 ) ;
/* simple typedef */
TEST_BTF_C ( str , uint64_t , 0 , 100 ) ;
TEST_BTF ( str , u64 , BTF_F_NONAME , " 1 " , 1 ) ;
/* zero value should be printed at toplevel */
TEST_BTF ( str , u64 , 0 , " (u64)0 " , 0 ) ;
TEST_BTF ( str , u64 , BTF_F_NONAME , " 0 " , 0 ) ;
TEST_BTF ( str , u64 , BTF_F_ZERO , " (u64)0 " , 0 ) ;
TEST_BTF ( str , u64 , BTF_F_NONAME | BTF_F_ZERO , " 0 " , 0 ) ;
/* typedef struct */
TEST_BTF_C ( str , atomic_t , 0 , { . counter = ( int ) 1 , } ) ;
TEST_BTF ( str , atomic_t , BTF_F_NONAME , " {1,} " , { . counter = 1 , } ) ;
/* typedef with 0 value should be printed at toplevel */
TEST_BTF ( str , atomic_t , 0 , " (atomic_t){} " , { . counter = 0 , } ) ;
TEST_BTF ( str , atomic_t , BTF_F_NONAME , " {} " , { . counter = 0 , } ) ;
TEST_BTF ( str , atomic_t , BTF_F_ZERO , " (atomic_t){.counter = (int)0,} " ,
{ . counter = 0 , } ) ;
TEST_BTF ( str , atomic_t , BTF_F_NONAME | BTF_F_ZERO ,
" {0,} " , { . counter = 0 , } ) ;
/* enum where enum value does (and does not) exist */
TEST_BTF_C ( str , enum bpf_cmd , 0 , BPF_MAP_CREATE ) ;
TEST_BTF ( str , enum bpf_cmd , 0 , " (enum bpf_cmd)BPF_MAP_CREATE " , 0 ) ;
TEST_BTF ( str , enum bpf_cmd , BTF_F_NONAME , " BPF_MAP_CREATE " ,
BPF_MAP_CREATE ) ;
TEST_BTF ( str , enum bpf_cmd , BTF_F_NONAME | BTF_F_ZERO ,
" BPF_MAP_CREATE " , 0 ) ;
TEST_BTF ( str , enum bpf_cmd , BTF_F_ZERO , " (enum bpf_cmd)BPF_MAP_CREATE " ,
BPF_MAP_CREATE ) ;
TEST_BTF ( str , enum bpf_cmd , BTF_F_NONAME | BTF_F_ZERO ,
" BPF_MAP_CREATE " , BPF_MAP_CREATE ) ;
TEST_BTF_C ( str , enum bpf_cmd , 0 , 2000 ) ;
TEST_BTF ( str , enum bpf_cmd , BTF_F_NONAME , " 2000 " , 2000 ) ;
/* simple struct */
TEST_BTF_C ( str , struct btf_enum , 0 ,
{ . name_off = ( __u32 ) 3 , . val = ( __s32 ) - 1 , } ) ;
TEST_BTF ( str , struct btf_enum , BTF_F_NONAME , " {3,-1,} " ,
{ . name_off = 3 , . val = - 1 , } ) ;
TEST_BTF ( str , struct btf_enum , BTF_F_NONAME , " {-1,} " ,
{ . name_off = 0 , . val = - 1 , } ) ;
TEST_BTF ( str , struct btf_enum , BTF_F_NONAME | BTF_F_ZERO , " {0,-1,} " ,
{ . name_off = 0 , . val = - 1 , } ) ;
/* empty struct should be printed */
TEST_BTF ( str , struct btf_enum , 0 , " (struct btf_enum){} " ,
{ . name_off = 0 , . val = 0 , } ) ;
TEST_BTF ( str , struct btf_enum , BTF_F_NONAME , " {} " ,
{ . name_off = 0 , . val = 0 , } ) ;
TEST_BTF ( str , struct btf_enum , BTF_F_ZERO ,
" (struct btf_enum){.name_off = (__u32)0,.val = (__s32)0,} " ,
{ . name_off = 0 , . val = 0 , } ) ;
/* struct with pointers */
TEST_BTF ( str , struct list_head , BTF_F_PTR_RAW ,
" (struct list_head){.next = (struct list_head *)0x0000000000000001,} " ,
{ . next = ( struct list_head * ) 1 } ) ;
/* NULL pointer should not be displayed */
TEST_BTF ( str , struct list_head , BTF_F_PTR_RAW ,
" (struct list_head){} " ,
{ . next = ( struct list_head * ) 0 } ) ;
/* struct with char array */
TEST_BTF ( str , struct bpf_prog_info , 0 ,
" (struct bpf_prog_info){.name = (char[])['f','o','o',],} " ,
{ . name = " foo " , } ) ;
TEST_BTF ( str , struct bpf_prog_info , BTF_F_NONAME ,
" {['f','o','o',],} " ,
{ . name = " foo " , } ) ;
/* leading null char means do not display string */
TEST_BTF ( str , struct bpf_prog_info , 0 ,
" (struct bpf_prog_info){} " ,
{ . name = { ' \0 ' , ' f ' , ' o ' , ' o ' } } ) ;
/* handle non-printable characters */
TEST_BTF ( str , struct bpf_prog_info , 0 ,
" (struct bpf_prog_info){.name = (char[])[1,2,3,],} " ,
{ . name = { 1 , 2 , 3 , 0 } } ) ;
/* struct with non-char array */
TEST_BTF ( str , struct __sk_buff , 0 ,
" (struct __sk_buff){.cb = (__u32[])[1,2,3,4,5,],} " ,
{ . cb = { 1 , 2 , 3 , 4 , 5 , } , } ) ;
TEST_BTF ( str , struct __sk_buff , BTF_F_NONAME ,
" {[1,2,3,4,5,],} " ,
{ . cb = { 1 , 2 , 3 , 4 , 5 } , } ) ;
/* For non-char, arrays, show non-zero values only */
TEST_BTF ( str , struct __sk_buff , 0 ,
" (struct __sk_buff){.cb = (__u32[])[1,],} " ,
{ . cb = { 0 , 0 , 1 , 0 , 0 } , } ) ;
/* struct with bitfields */
TEST_BTF_C ( str , struct bpf_insn , 0 ,
{ . code = ( __u8 ) 1 , . dst_reg = ( __u8 ) 0x2 , . src_reg = ( __u8 ) 0x3 , . off = ( __s16 ) 4 , . imm = ( __s32 ) 5 , } ) ;
TEST_BTF ( str , struct bpf_insn , BTF_F_NONAME , " {1,0x2,0x3,4,5,} " ,
{ . code = 1 , . dst_reg = 0x2 , . src_reg = 0x3 , . off = 4 ,
. imm = 5 , } ) ;
# else
skip = true ;
# endif
return 0 ;
}
char _license [ ] SEC ( " license " ) = " GPL " ;