From c5233bf49190a3b543ded9e64c027ae04aaf104c Mon Sep 17 00:00:00 2001 From: Nadav Strahilevitz Date: Thu, 5 Jan 2023 14:31:58 +0000 Subject: [PATCH] lazy ksymbols: fix address query with reverse linear search In commit 3ea92310, a new KernelSymbolsTable implementation was added which uses lazy querying and parsing of a stored string copy of the /proc/kallsyms file. In order to keep efficiency of non cached queries, binary search was used for querying symbols by address, since the file mostly stores the symbols in order of addresses. However, on kernels compiled with CONFIG_KALLSYMS_ALL=y, symbols from other sections may be present in a non ordered place. This wasn't taken in consideration in initial implementations, and so a query by address could have failed despite existing. These symbols usually appear near the end of the file, so a reverse linear search is added after a failed binary search. --- helpers/kernel_symbols.go | 101 +++++++++++++++++++++----------------- 1 file changed, 57 insertions(+), 44 deletions(-) diff --git a/helpers/kernel_symbols.go b/helpers/kernel_symbols.go index 01d38078..74fd4646 100644 --- a/helpers/kernel_symbols.go +++ b/helpers/kernel_symbols.go @@ -51,6 +51,22 @@ func symbolKey(owner, name string) string { return owner + "_" + name } +// parses ksymbol line file file. returns in order the symbols "type", "name", "order" +func parseSymbolLine(line []string) (string, string, string) { + symbolType := strings.Clone(line[1]) + symbolName := strings.Clone(line[2]) + + symbolOwner := "system" + if len(line) > 3 { + // When a symbol is contained in a kernel module, it will be specified + // within square brackets, otherwise it's part of the system + symbolOwner = strings.Clone(line[3]) + symbolOwner = strings.TrimPrefix(symbolOwner, "[") + symbolOwner = strings.TrimSuffix(symbolOwner, "]") + } + return symbolType, symbolName, symbolOwner +} + // fullKernelSymbolTable type fullKernelSymbolTable struct { @@ -116,17 +132,8 @@ func (k *fullKernelSymbolTable) Refresh() error { if err != nil { continue } - symbolType := strings.Clone(line[1]) - symbolName := strings.Clone(line[2]) - - symbolOwner := "system" - if len(line) > 3 { - // When a symbol is contained in a kernel module, it will be specified - // within square brackets, otherwise it's part of the system - symbolOwner = strings.Clone(line[3]) - symbolOwner = strings.TrimPrefix(symbolOwner, "[") - symbolOwner = strings.TrimSuffix(symbolOwner, "]") - } + + symbolType, symbolName, symbolOwner := parseSymbolLine(line) symbolKey := symbolOwner + "_" + symbolName symbol := &KernelSymbol{symbolName, symbolType, symbolAddr, symbolOwner} @@ -199,17 +206,8 @@ func (k *lazyKernelSymbols) GetSymbolByName(owner string, name string) (*KernelS if err != nil { continue } - symbolType := strings.Clone(line[1]) - symbolName := strings.Clone(line[2]) - - symbolOwner := "system" - if len(line) > 3 { - // When a symbol is contained in a kernel module, it will be specified - // within square brackets, otherwise it's part of the system - symbolOwner = strings.Clone(line[3]) - symbolOwner = strings.TrimPrefix(symbolOwner, "[") - symbolOwner = strings.TrimSuffix(symbolOwner, "]") - } + + symbolType, symbolName, symbolOwner := parseSymbolLine(line) if name == symbolName && owner == symbolOwner { symbolKey := symbolKey(symbolOwner, symbolName) @@ -233,8 +231,10 @@ func (k *lazyKernelSymbols) GetSymbolByAddr(addr uint64) (*KernelSymbol, error) err error ) - // since kallsyms is sorted in address ascending order, use binary search - i := sort.Search(len(k.fileContent), func(i int) bool { + fileLen := len(k.fileContent) + found := false + // kallsyms are almost sorted by address, start search with binary search + i := sort.Search(fileLen, func(i int) bool { line := strings.Fields(k.fileContent[i]) if len(line) < 3 { return false @@ -243,6 +243,34 @@ func (k *lazyKernelSymbols) GetSymbolByAddr(addr uint64) (*KernelSymbol, error) if err != nil { return false } + if symbolAddr == addr { + found = true + + symbolType, symbolName, symbolOwner := parseSymbolLine(line) + + symbolKey := symbolKey(symbolOwner, symbolName) + symbol = &KernelSymbol{symbolName, symbolType, symbolAddr, symbolOwner} + k.symbolMap[symbolKey] = symbol + k.symbolAddrMap[symbolAddr] = symbol + return true + } + return symbolAddr > addr + }) + + if i < len(k.fileContent) && found { + return symbol, nil + } + + // symbols may be out of order near the end of the ksymbols, search linearly in reverse + for i := fileLen - 1; i > 0; i-- { + line := strings.Fields(k.fileContent[i]) + if len(line) < 3 { + continue + } + symbolAddr, err = strconv.ParseUint(line[0], 16, 64) + if err != nil { + continue + } if symbolAddr == addr { symbolType := strings.Clone(line[1]) symbolName := strings.Clone(line[2]) @@ -257,19 +285,13 @@ func (k *lazyKernelSymbols) GetSymbolByAddr(addr uint64) (*KernelSymbol, error) } symbolKey := symbolKey(symbolOwner, symbolName) - symbol = &KernelSymbol{symbolName, symbolType, symbolAddr, symbolOwner} + symbol := &KernelSymbol{symbolName, symbolType, symbolAddr, symbolOwner} k.symbolMap[symbolKey] = symbol k.symbolAddrMap[symbolAddr] = symbol - return true + return symbol, nil } - return symbolAddr > addr - }) - - if i < len(k.fileContent) && symbolAddr == addr { - return symbol, nil - } else { - return nil, SymbolNotFoundAtAddress(addr) } + return nil, SymbolNotFoundAtAddress(addr) } func (k *lazyKernelSymbols) Refresh() error { @@ -306,17 +328,8 @@ func (k *lazyKernelSymbols) getSymbolByAddrNotBinary(addr uint64) (*KernelSymbol if symbolAddr != addr { continue } - symbolType := strings.Clone(line[1]) - symbolName := strings.Clone(line[2]) - - symbolOwner := "system" - if len(line) > 3 { - // When a symbol is contained in a kernel module, it will be specified - // within square brackets, otherwise it's part of the system - symbolOwner = strings.Clone(line[3]) - symbolOwner = strings.TrimPrefix(symbolOwner, "[") - symbolOwner = strings.TrimSuffix(symbolOwner, "]") - } + + symbolType, symbolName, symbolOwner := parseSymbolLine(line) symbolKey := symbolKey(symbolOwner, symbolName) symbol := &KernelSymbol{symbolName, symbolType, symbolAddr, symbolOwner}