Модуль символьного выполнения для проверки достижимости путей в анализе помеченных данных. Использует Z3 SMT-решатель для фильтрации нереализуемых путей, снижая уровень ложных срабатываний до 1-3%.
Обзор¶
Когда анализ помеченных данных (анализ заражения данных) находит путь от источника к приёмнику, путь может проходить через ветвления с противоречивыми условиями (например, if (x > 10) и далее if (x < 5) для той же переменной). Символьное выполнение проверяет реализуемость таких путей, кодируя условия ветвлений как ограничения Z3 и вызывая решатель.
Ключевые характеристики: - Опциональный компонент — корректно деградирует при отсутствии Z3 - Дополнительные затраты: ~20-25% времени, ~25-30% памяти - Максимум 20 ограничений на путь (настраивается) - Рекурсивный нисходящий анализатор выражений условий - Поддержка целочисленных сравнений, булевой логики, арифметики, моделей функций, проверок NULL - Определение направления ветки через анализ преемников CFG - Настраиваемые таймауты решателя (500 мс для LIA, 2 с для UF-запросов)
Установка¶
pip install z3-solver
Z3 включён в requirements.txt. Если Z3 недоступен во время выполнения, система записывает предупреждение и возвращает консервативные оценки (все пути считаются реализуемыми).
Архитектура¶
TaintPropagator (enable_symbolic_execution=True)
│
├─ find_taint_paths() → необработанные пути
│
└─ PathConstraintTracker.filter_infeasible_paths()
│
├─ _collect_constraints(start, end)
│ ├─ Обход рёбер CFG, извлечение условий из CDG
│ └─ _determine_branch_direction() через преемников CFG
│
├─ _parse_condition(code, z3_vars, state)
│ └─ Рекурсивный спуск: or → and → not → comparison → additive → atom
│ ├─ Переменные: z3.Int('x')
│ ├─ Функции: strlen(s) → новая Int с ограничениями домена
│ └─ NULL: z3.Bool('ptr_is_null')
│
└─ z3.Solver().check() (с таймаутом)
├─ sat → путь реализуем (достоверность 0.9)
├─ unsat → путь нереализуем, ОТФИЛЬТРОВАТЬ (достоверность 0.95)
└─ unknown → консервативно, сохранить путь (достоверность 0.4)
Справочник API¶
PathConstraint¶
@dataclass
class PathConstraint:
node_id: int # ID узла CPG (точка ветвления)
condition: str # Выражение условия (например, "x > 100")
location: str # Расположение файл:строка
negated: bool # True для else-ветки
SymbolicExecutionConfig¶
@dataclass
class SymbolicExecutionConfig:
enabled: bool = True
max_constraints: int = 20
solver_timeout_ms: int = 500 # Запросы QF_LIA
solver_timeout_uf_ms: int = 2000 # Запросы с неинтерпретируемыми функциями
max_parse_depth: int = 10 # Ограничение глубины рекурсивного спуска
enable_function_models: bool = True # Модели strlen, sizeof, len
enable_arithmetic: bool = True # +, -, * в условиях
PathConstraintTracker¶
class PathConstraintTracker:
def __init__(self, cpg_service, max_constraints: int = 20,
config: Optional[SymbolicExecutionConfig] = None):
"""
Аргументы:
cpg_service: экземпляр CPGQueryService для запросов к графу
max_constraints: максимум ограничений на путь (по умолчанию: 20).
Пути с большим числом ограничений считаются реализуемыми.
config: необязательная конфигурация таймаутов и функций решателя.
"""
def is_path_feasible(self, start_node: int, end_node: int) -> Tuple[bool, float]:
"""
Проверить реализуемость пути между двумя узлами.
Возвращает:
(is_feasible, confidence) — достоверность от 0.0 до 1.0
"""
def filter_infeasible_paths(
self, paths: List[Any], min_confidence: float = 0.8
) -> List[Any]:
"""
Отфильтровать нереализуемые пути из списка.
Аргументы:
paths: список объектов путей (должны иметь атрибут .path_nodes —
список ID узлов; первый элемент = источник, последний = приёмник)
min_confidence: минимальная достоверность (по умолчанию: 0.8)
Возвращает:
Отфильтрованный список — только реализуемые пути
"""
def get_statistics(self) -> Dict[str, Any]:
"""
Получить статистику решателя.
Возвращает:
Словарь с ключами:
- z3_available (bool): установлен ли Z3
- max_constraints (int): максимум ограничений на путь
- solver_timeout_ms (int): таймаут LIA-запросов
- solver_timeout_uf_ms (int): таймаут UF-запросов
- enable_function_models (bool): модели функций включены
- enable_arithmetic (bool): арифметика включена
- supported_functions (list): известные модели функций
"""
Использование¶
Прямой вызов API¶
from src.analysis.dataflow.symbolic_execution import PathConstraintTracker
tracker = PathConstraintTracker(cpg_service, max_constraints=20)
# Проверка одного пути
is_feasible, confidence = tracker.is_path_feasible(node_123, node_456)
if not is_feasible and confidence > 0.9:
print("Путь нереализуем — вероятно ложное срабатывание")
# Фильтрация пакета путей
feasible_paths = tracker.filter_infeasible_paths(all_paths, min_confidence=0.8)
Интеграция с анализом помеченных данных¶
from src.analysis.dataflow.taint_analysis import TaintPropagator
propagator = TaintPropagator(
cpg_service,
enable_symbolic_execution=True # Включает Z3-фильтрацию
)
# Символьное выполнение применяется автоматически
paths = propagator.find_taint_paths(
sources=["recv", "getenv"],
sinks=["strcpy", "sprintf"]
)
# paths уже отфильтрованы — нереализуемые пути удалены
Поддерживаемые типы ограничений¶
| Ограничение | Пример | Кодирование в Z3 |
|---|---|---|
| Целочисленное (переменная и константа) | x > 100 |
z3.Int('x') > 100 |
| Целочисленное (переменная и переменная) | a > b |
z3.Int('a') > z3.Int('b') |
| Равенство | x == 0 |
z3.Int('x') == 0 |
| Неравенство | x != 5 |
z3.Int('x') != 5 |
| Булев флаг | flag |
z3.Int('flag') != 0 |
| Логическое И | x > 0 && y < 10 |
z3.And(x > 0, y < 10) |
| Логическое ИЛИ | x > 0 \|\| y < 10 |
z3.Or(x > 0, y < 10) |
| Логическое НЕ | !flag |
z3.Not(flag != 0) |
| Арифметика | x + 1 > 10 |
z3.Int('x') + 1 > 10 |
| Модель функции | strlen(s) > 10 |
z3.Int('strlen_s') > 10 (с strlen_s >= 0) |
| Проверка NULL | ptr == NULL |
z3.Bool('ptr_is_null') == True |
| Проверка nullptr | ptr != nullptr |
z3.Bool('ptr_is_null') == False |
Не поддерживается: сравнения с плавающей точкой, строковые операции, межпроцедурные ограничения.
Модели функций¶
Известные чистые функции моделируются как неинтерпретируемые функции с ограничениями домена:
| Функция | Ограничение домена | Языки |
|---|---|---|
strlen, strnlen, wcslen |
result >= 0 |
C/C++ |
sizeof |
result > 0 |
C/C++ |
len, length |
result >= 0 |
Go, Python, Java |
size, count, cap |
result >= 0 |
Go, C++ STL |
abs |
result >= 0 |
Все |
Одна и та же функция с тем же аргументом использует одну Z3-переменную, что позволяет обнаруживать противоречия (например, strlen(s) > 10 && strlen(s) < 5 → нереализуемо).
Уровни достоверности¶
| Результат решателя | Реализуем? | Достоверность | Действие |
|---|---|---|---|
sat |
Да | 0.9 | Сохранить путь |
unsat |
Нет | 0.95 | Удалить (ложное срабатывание) |
unknown |
Предположительно да | 0.4 | Сохранить (консервативно) |
| Z3 недоступен | Предположительно да | 0.5 | Сохранить (деградированный режим) |
| Слишком много ограничений | Предположительно да | 0.3 | Сохранить (пропуск анализа) |
Конфигурация¶
Через config.yaml:
symbolic_execution:
enabled: true
max_constraints: 20
solver_timeout_ms: 500
solver_timeout_uf_ms: 2000
max_parse_depth: 10
enable_function_models: true
enable_arithmetic: true
Влияние на производительность¶
| Метрика | Без Z3 | С Z3 | Разница |
|---|---|---|---|
| Время анализа | Базовое | +20-25% | Нагрузка решателя |
| Память | Базовая | +25-30% | Внутренние структуры Z3 |
| Ложные срабатывания | ~12% | ~1-3% | Нереализуемые пути удалены |
Ограничения¶
- Облегчённый анализ — не полноценный символьный исполнитель; отслеживает только условия ветвлений
- Без плавающей точки — нет поддержки
float/double(замедление Z3 в 100-1000 раз) - Без строковых операций — нет
strcmp, равенства строк (замедление Z3 в 50-500 раз) - Максимум 20 ограничений — длинные пути считаются реализуемыми
- Без межпроцедурного анализа — ограничения собираются в пределах одной функции
- Только линейная арифметика —
var * constподдерживается,var * var— нет
Связанная документация¶
- Руководство по анализу потоков данных — интеграция с анализом заражения данных
- Модули анализа — модули CFG, DDG, CDG
- Безопасность — обзор анализа безопасности
- Система гипотез — валидация гипотез безопасности
Модуль: src/analysis/dataflow/symbolic_execution.py
Последнее обновление: март 2026