Символьное выполнение (Z3 SMT Solver)¶
Модуль символьного выполнения для проверки достижимости путей в анализе помеченных данных. Использует Z3 SMT-решатель для фильтрации нереализуемых путей, снижая уровень ложных срабатываний на 5-7%.
Обзор¶
Когда анализ помеченных данных (анализ заражения данных) находит путь от источника к приёмнику, путь может проходить через ветвления с противоречивыми условиями (например, if (x > 10) и далее if (x < 5) для той же переменной). Символьное выполнение проверяет реализуемость таких путей, кодируя условия ветвлений как ограничения Z3 и вызывая решатель.
Ключевые характеристики: - Опциональный компонент — корректно деградирует при отсутствии Z3 - Дополнительные затраты: ~20% времени, ~25% памяти - Максимум 20 ограничений на путь (настраивается) - Поддержка целочисленных сравнений, проверок NULL, простой арифметики
Установка¶
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
│
├─ _parse_constraint_to_z3(constraint, z3_vars)
│ └─ Преобразование "x > 100" → z3.Int('x') > 100
│
└─ 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-ветки
PathConstraintTracker¶
class PathConstraintTracker:
def __init__(self, cpg_service, max_constraints: int = 20):
"""
Аргументы:
cpg_service: экземпляр CPGQueryService для запросов к графу
max_constraints: максимум ограничений на путь (по умолчанию: 20).
Пути с большим числом ограничений считаются реализуемыми.
"""
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: список объектов путей (должны иметь .source_id и .sink_id)
min_confidence: минимальная достоверность (по умолчанию: 0.8)
Возвращает:
Отфильтрованный список — только реализуемые пути
"""
Использование¶
Прямой вызов 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 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 |
| Равенство | x == 0 |
z3.Int('x') == 0 |
| Неравенство | x != 5 |
z3.Int('x') != 5 |
| Проверка NULL | ptr == NULL |
z3.Int('ptr') == 0 |
| Проверка nullptr | ptr != nullptr |
z3.Int('ptr') != 0 |
| Простая арифметика | x + y > z |
z3.Int('x') + z3.Int('y') > z3.Int('z') |
Не поддерживается: вызовы функций в условиях, сложные выражения, числа с плавающей точкой, строковые операции.
Уровни достоверности¶
| Результат решателя | Реализуем? | Достоверность | Действие |
|---|---|---|---|
sat |
Да | 0.9 | Сохранить путь |
unsat |
Нет | 0.95 | Удалить (ложное срабатывание) |
unknown |
Предположительно да | 0.4 | Сохранить (консервативно) |
| Z3 недоступен | Предположительно да | 0.5 | Сохранить (деградированный режим) |
| Слишком много ограничений | Предположительно да | 0.5 | Сохранить (пропуск анализа) |
Влияние на производительность¶
| Метрика | Без Z3 | С Z3 | Разница |
|---|---|---|---|
| Время анализа | Базовое | +20% | Нагрузка решателя |
| Память | Базовая | +25% | Внутренние структуры Z3 |
| Ложные срабатывания | ~12% | ~5-8% | Нереализуемые пути удалены |
Ограничения¶
- Облегчённый анализ — не полноценный символьный исполнитель; отслеживает только условия ветвлений
- Только целые числа — нет поддержки чисел с плавающей точкой и строк
- Максимум 20 ограничений — длинные пути считаются реализуемыми
- Без межпроцедурного анализа — ограничения собираются в пределах одной функции
- Разбор по шаблонам — условия анализируются через регулярные выражения, не полный AST
Связанная документация¶
- Руководство по анализу потоков данных — интеграция с анализом заражения данных
- Модули анализа — модули CFG, DDG, CDG
- Безопасность — обзор анализа безопасности
- Система гипотез — валидация гипотез безопасности
Модуль: src/analysis/dataflow/symbolic_execution.py
Последнее обновление: февраль 2026