github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/kallsyms/kallsyms_test.go (about)

     1  // Copyright 2023 The Inspektor Gadget authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package kallsyms
    16  
    17  import (
    18  	"os"
    19  	"strings"
    20  	"testing"
    21  
    22  	"github.com/cilium/ebpf"
    23  	"github.com/cilium/ebpf/btf"
    24  	"github.com/stretchr/testify/require"
    25  
    26  	utilstest "github.com/inspektor-gadget/inspektor-gadget/internal/test"
    27  )
    28  
    29  func resetState() {
    30  	symbolsMap = map[string]uint64{}
    31  	triedGetAddr = map[string]error{}
    32  }
    33  
    34  func TestCustomKAllSyms(t *testing.T) {
    35  	kAllSymsStr := strings.Join([]string{
    36  		"0000000000000000 A fixed_percpu_data",
    37  		"ffffffffb4231f40 D bpf_prog_fops",
    38  		"ffffffffb43723e0 d socket_file_ops",
    39  	}, "\n")
    40  
    41  	kAllSymsReader := strings.NewReader(kAllSymsStr)
    42  	kAllSyms, err := NewKAllSymsFromReader(kAllSymsReader)
    43  	require.Nil(t, err, "NewKAllSymsFromReader failed: %v", err)
    44  	require.True(t, kAllSyms.SymbolExists("bpf_prog_fops"),
    45  		"SymbolExists should have found bpf_prog_fops")
    46  	require.False(t, kAllSyms.SymbolExists("abcde_bad_name"),
    47  		"SymbolExists should not have found abcde_bad_name")
    48  
    49  	lookupByInstructionPointerTests := []struct {
    50  		instructionPointer uint64
    51  		expectedSymbol     string
    52  	}{
    53  		{0, "fixed_percpu_data"},
    54  		{0xffffffffb4231f39, "fixed_percpu_data"},
    55  		{0xffffffffb4231f40, "bpf_prog_fops"},
    56  		// TODO: is it correct? should it be bpf_prog_fops?
    57  		{0xffffffffb4231f41, "bpf_prog_fops"},
    58  		{0xffffffffb43723df, "bpf_prog_fops"},
    59  		{0xffffffffb43723e0, "socket_file_ops"},
    60  		{0xffffffffb43723e1, "socket_file_ops"},
    61  	}
    62  	for _, tt := range lookupByInstructionPointerTests {
    63  		require.Equal(t, tt.expectedSymbol, kAllSyms.LookupByInstructionPointer(tt.instructionPointer),
    64  			"LookupByInstructionPointer(0x%x)", tt.instructionPointer)
    65  	}
    66  }
    67  
    68  func TestRealKAllSyms(t *testing.T) {
    69  	utilstest.RequireRoot(t)
    70  	utilstest.RequireFileContains(t, "/proc/kallsyms", "bpf_prog_fops")
    71  	utilstest.RequireFileContains(t, "/proc/kallsyms", "socket_file_ops")
    72  
    73  	kAllSyms, err := NewKAllSyms()
    74  	require.Nil(t, err, "NewKAllSyms failed: %v", err)
    75  	require.True(t, kAllSyms.SymbolExists("bpf_prog_fops"),
    76  		"SymbolExists should have found bpf_prog_fops")
    77  	require.False(t, kAllSyms.SymbolExists("abcde_bad_name"),
    78  		"SymbolExists should not have found abcde_bad_name")
    79  
    80  	addr, ok := kAllSyms.symbolsMap["bpf_prog_fops"]
    81  	require.True(t, ok, "bpf_prog_fops not found in symbolsMap")
    82  	// /proc/kallsyms contains the address 0 without CAP_SYSLOG.
    83  	// Since we use RequireRoot, it should not happen.
    84  	require.NotEqual(t, 0, addr, "bpf_prog_fops has a zero address")
    85  }
    86  
    87  var specTest = &ebpf.CollectionSpec{
    88  	Maps: map[string]*ebpf.MapSpec{
    89  		".rodata": {
    90  			Type:       ebpf.Array,
    91  			KeySize:    4,
    92  			ValueSize:  4,
    93  			MaxEntries: 1,
    94  			Value: &btf.Datasec{
    95  				Vars: []btf.VarSecinfo{
    96  					{
    97  						Type: &btf.Var{
    98  							Name: "bpf_prog_fops_addr",
    99  							Type: &btf.Int{Size: 8},
   100  						},
   101  						Offset: 0,
   102  						Size:   8,
   103  					},
   104  					{
   105  						Type: &btf.Var{
   106  							Name: "socket_file_ops_addr",
   107  							Type: &btf.Int{Size: 8},
   108  						},
   109  						Offset: 8,
   110  						Size:   8,
   111  					},
   112  				},
   113  			},
   114  			Contents: []ebpf.MapKV{
   115  				{Key: uint32(0), Value: []byte{
   116  					0, 0, 0, 0, 0, 0, 0, 0,
   117  					0, 0, 0, 0, 0, 0, 0, 0,
   118  				}},
   119  			},
   120  		},
   121  	},
   122  }
   123  
   124  func TestSpecRewriteConstants(t *testing.T) {
   125  	t.Cleanup(resetState)
   126  
   127  	var err error
   128  	kAllSymsStr := strings.Join([]string{
   129  		"0000000000000000 A fixed_percpu_data",
   130  		"ffffffffb4231f40 D bpf_prog_fops",
   131  		"ffffffffb43723e0 d socket_file_ops",
   132  	},
   133  		"\n")
   134  	kAllSymsReader := strings.NewReader(kAllSymsStr)
   135  	kAllSyms, err := NewKAllSymsFromReader(kAllSymsReader)
   136  	require.Nil(t, err, "NewKAllSymsFromReader failed: %v", err)
   137  	kSymbolResolver := newKAllSymsResolver()
   138  	kSymbolResolver.kAllSyms = kAllSyms
   139  
   140  	// Little endian representation of the addresses above:
   141  	bpfProgFopsAddr := []byte{0x40, 0x1f, 0x23, 0xb4, 0xff, 0xff, 0xff, 0xff}
   142  	socketFileOpsAddr := []byte{0xe0, 0x23, 0x37, 0xb4, 0xff, 0xff, 0xff, 0xff}
   143  
   144  	spec := specTest.Copy()
   145  
   146  	err = specUpdateAddresses(
   147  		[]symbolResolver{
   148  			kSymbolResolver,
   149  		},
   150  		spec,
   151  		[]string{"abcde_bad_name"},
   152  	)
   153  	require.ErrorContainsf(t, err, "was not found in kallsyms", "specUpdateAddresses should have failed")
   154  
   155  	err = specUpdateAddresses(
   156  		[]symbolResolver{
   157  			kSymbolResolver,
   158  		},
   159  		spec,
   160  		[]string{"bpf_prog_fops", "socket_file_ops"},
   161  	)
   162  	require.Nil(t, err, "specUpdateAddresses failed: %v", err)
   163  
   164  	expectedContents := []byte{}
   165  	expectedContents = append(expectedContents, bpfProgFopsAddr...)
   166  	expectedContents = append(expectedContents, socketFileOpsAddr...)
   167  	contents := spec.Maps[".rodata"].Contents[0].Value
   168  	require.Equal(t, contents, expectedContents, "contents aren't equal")
   169  }
   170  
   171  func TestSpecRewriteConstantsRoot(t *testing.T) {
   172  	utilstest.RequireRoot(t)
   173  	utilstest.RequireFileContains(t, "/proc/kallsyms", "bpf_prog_fops")
   174  	utilstest.RequireFileContains(t, "/proc/kallsyms", "socket_file_ops")
   175  	t.Cleanup(resetState)
   176  
   177  	resetState()
   178  
   179  	spec1 := specTest.Copy()
   180  	err := specUpdateAddresses(
   181  		[]symbolResolver{
   182  			newKAllSymsResolver(),
   183  		},
   184  		spec1,
   185  		[]string{"bpf_prog_fops", "socket_file_ops"},
   186  	)
   187  	require.Nil(t, err, "specUpdateAddresses failed: %v", err)
   188  
   189  	resetState()
   190  
   191  	spec2 := specTest.Copy()
   192  	err = specUpdateAddresses(
   193  		[]symbolResolver{
   194  			newEbpfResolver(),
   195  		},
   196  		spec2,
   197  		[]string{"bpf_prog_fops", "socket_file_ops"},
   198  	)
   199  	require.Nil(t, err, "specUpdateAddresses failed: %v", err)
   200  
   201  	resetState()
   202  
   203  	spec3 := specTest.Copy()
   204  	err = specUpdateAddresses(
   205  		[]symbolResolver{
   206  			newKAllSymsResolver(),
   207  			newEbpfResolver(),
   208  		},
   209  		spec3,
   210  		[]string{"bpf_prog_fops", "socket_file_ops"},
   211  	)
   212  	require.Nil(t, err, "specUpdateAddresses failed: %v", err)
   213  
   214  	resetState()
   215  
   216  	spec4 := specTest.Copy()
   217  	err = specUpdateAddresses(
   218  		[]symbolResolver{},
   219  		spec4,
   220  		[]string{"bpf_prog_fops", "socket_file_ops"},
   221  	)
   222  	require.ErrorIs(t, err, os.ErrNotExist, "specUpdateAddresses should have failed")
   223  
   224  	t.Logf("spec1: %v", spec1.Maps[".rodata"].Contents[0].Value)
   225  	t.Logf("spec2: %v", spec2.Maps[".rodata"].Contents[0].Value)
   226  	t.Logf("spec3: %v", spec3.Maps[".rodata"].Contents[0].Value)
   227  
   228  	require.Equal(t,
   229  		spec1.Maps[".rodata"].Contents[0].Value,
   230  		spec3.Maps[".rodata"].Contents[0].Value,
   231  		"contents spec1 and spec3 aren't equal")
   232  	require.Equal(t,
   233  		spec1.Maps[".rodata"].Contents[0].Value,
   234  		spec2.Maps[".rodata"].Contents[0].Value,
   235  		"contents spec1 and spec2 aren't equal")
   236  }
   237  
   238  func TestSpecRewriteConstantsWithEbpf(t *testing.T) {
   239  	utilstest.RequireRoot(t)
   240  	t.Cleanup(resetState)
   241  
   242  	spec := specTest.Copy()
   243  	err := specUpdateAddresses(
   244  		[]symbolResolver{
   245  			newEbpfResolver(),
   246  		},
   247  		spec,
   248  		[]string{"bpf_prog_fops", "socket_file_ops"},
   249  	)
   250  	require.Nil(t, err, "SpecUpdateAddresses failed: %v", err)
   251  	require.Len(t, spec.Maps[".rodata"].Contents[0].Value, 16, "Contents should have 16 bytes")
   252  	values, ok := spec.Maps[".rodata"].Contents[0].Value.([]byte)
   253  	require.True(t, ok, "Contents should be a byte slice")
   254  	emptyBytes := []byte{0, 0, 0, 0, 0, 0, 0, 0}
   255  	require.NotEqual(t, emptyBytes, values[:8], "First byte should not be zero")
   256  	require.NotEqual(t, emptyBytes, values[8:], "Last 8 bytes should be zero")
   257  }