Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion cle/backends/elf/elf.py
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,11 @@ def get_symbol(self, symid, symbol_table=None): # pylint: disable=arguments-dif
return self._nullsymbol
if symbol_table is None:
raise TypeError("Must specify the symbol table to look up symbols by index")
# pyelftools does not bounds-check indices for section-backed symbol tables. The synthetic table built
# from PT_DYNAMIC has no section type and its inferred size is not reliable.
symbol_table_type = symbol_table.header.get("sh_type")
if symid < 0 or (symbol_table_type in ("SHT_SYMTAB", "SHT_DYNSYM") and symid >= symbol_table.num_symbols()):
return None
try:
re_sym = symbol_table.get_symbol(symid)
except Exception: # pylint: disable=broad-except
Expand Down Expand Up @@ -476,7 +481,7 @@ def get_symbol(self, symid, symbol_table=None): # pylint: disable=arguments-dif
symbol = ELFSymbol(self, re_sym)
if version is not None and self._versions is not None:
version = enums.ENUM_VERSYM.get(version, version) & 0x7FFF
symbol.version = self._versions[version]
symbol.version = self._versions.get(version)
self._symbol_cache[cache_key] = symbol
self._cache_symbol_name(symbol)
return symbol
Expand Down
77 changes: 77 additions & 0 deletions tests/test_elf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from __future__ import annotations

from types import SimpleNamespace
from typing import Any, cast

import pytest

from cle.backends.elf import elf as elf_module
from cle.backends.elf.elf import ELF


class _SymbolTable:
"""Minimal pyelftools symbol table used to isolate ``ELF.get_symbol``."""

def __init__(self, count: int, symbol: Any = None, sh_type: str | None = "SHT_SYMTAB"):
self.count = count
self.symbol = symbol
self.lookups = []
self.header = {} if sh_type is None else {"sh_type": sh_type}

def num_symbols(self):
return self.count

def get_symbol(self, index):
self.lookups.append(index)
return self.symbol


@pytest.mark.parametrize("index", (-1, 3))
def test_out_of_range_symbol_index(index):
elf = cast(Any, ELF.__new__(ELF))
symbol_table = _SymbolTable(3)

assert elf.get_symbol(index, symbol_table) is None
assert not symbol_table.lookups


def test_unknown_symbol_version(monkeypatch):
elf = cast(Any, ELF.__new__(ELF))
symbol_table = _SymbolTable(3, object())
elf.hashtable = SimpleNamespace(symtab=symbol_table)
elf._vertable = SimpleNamespace(get_symbol=lambda _: SimpleNamespace(entry=SimpleNamespace(ndx=120)))
elf._versions = {0: "*local*", 1: "*global*"}
elf._symbol_cache = {}
elf._symbols_by_name = {}
elf._desperate_for_symbols = False
monkeypatch.setattr(ELF, "_symbol_to_tuple", staticmethod(lambda _: ("symbol",)))
monkeypatch.setattr(
elf_module,
"ELFSymbol",
lambda *_: SimpleNamespace(name="", version=None),
)

symbol = elf.get_symbol(2, symbol_table)

assert symbol.version is None
assert symbol_table.lookups == [2]


def test_synthetic_symbol_table_does_not_use_unreliable_count(monkeypatch):
elf = cast(Any, ELF.__new__(ELF))
symbol_table = _SymbolTable(1, object(), sh_type=None)
elf.hashtable = SimpleNamespace(symtab=symbol_table)
elf._vertable = None
elf._versions = None
elf._symbol_cache = {}
elf._symbols_by_name = {}
elf._desperate_for_symbols = False
monkeypatch.setattr(ELF, "_symbol_to_tuple", staticmethod(lambda _: ("symbol",)))
monkeypatch.setattr(
elf_module,
"ELFSymbol",
lambda *_: SimpleNamespace(name="", version=None),
)

assert elf.get_symbol(2, symbol_table) is not None
assert symbol_table.lookups == [2]
Loading