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 }