Как мы учились
чинить самолеты
в воздухе
Евгений Коломеец
eugene.kolomeetz@gmail.com
Copyright © 2017 Virtuozzo. All rights reserved.
Обзор
➢ Что такое “Live Patching”?
➢ Для чего это нужно?
➢ Состояние процесса
➢ Что мы можем патчить?
➢ Когда патч может быть приложен?
➢ Структура бинарного патча
➢ Динамические библиотеки
➢ Символы ELF
➢ Приложение патча
➢ Создание патча
2
Что такое “Live Patching”?
➢ Замена части “логики” в процессе
➢ Сохраняя состояние процесса
➢ Делая это безопасно
3
Для чего это нужно?
➢ Уменьшение времени простоя из-за критических уязвимостей за счёт:
➢Отсутствия рестарта сервисов
➢Нет необходимости в миграции
➢ Примеры утилит для live patching:
➢Kpatch, Kgraft, Ksplice для ядра
➢Pannus, Ksplice, LibCare для юзерспейса
4
Состояние процесса
➢ “Карточный домик”:
➢части сильно связаны между собой
➢ Изменяется во время выполнения
➢ Состоит из:
➢Статически выделенных переменных
➢Динамически выделенных переменных
➢Стека
5
Что мы можем патчить?
➢ Не любые изменения кода
➢ Фундаментальные ограничения:
➢Изменившийся размер данных
➢Другой порядок полей в структуре
➢ Требуется ревью кода :(
6
Когда патч может быть приложен?
➢ Требуется остановка процесса
➢ Не в любой момент времени:
➢Изменяемый код может выполняться
➢На него могут быть ссылки на стеке
➢ Необходим анализ стека:
➢ “поймать” процесс в “подходящем” месте
7
Когда патч может быть приложен?
8
g(void)
{
. . .
f();
}
f(void)
{
. . .
}
Когда патч может быть приложен?
9
g(void)
{
. . .
f();
}
f(void)
{
. . .
}
Поток остановлен здесь
Когда патч может быть приложен?
10
g(void)
{
. . .
f();
}
f(void)
{
. . .
}
Поток остановлен здесь
f()Вызывается новая версия
Когда патч может быть приложен?
11
g(void)
{
. . .
f();
}
f(void)
{
. . .
}
Поток остановлен здесь
f()Вызывается новая версия
,Возврат по адресу
сохранённому на стеке
Когда патч может быть приложен?
12
g(void)
{
. . .
f();
}
f(void)
{
. . .
}
Поток остановлен здесь
f()Вызывается новая версия
Исполняется код
старой версии f()
,Возврат по адресу
сохранённому на стеке
Структура бинарного патча
➢ Код заменяется на уровне функций:
➢Надёжно
➢Просто
➢ Формат: ELF dynamic shared object
➢Загружается в адресное пространство процесса
➢Дополнительная секция с метаданными
➢ Список адресов символов
➢ build-id
13
Динамическая библиотека: исходный код
14
extern int var;
int f(void)
{
return var + 1;
}
Динамическая библиотека: структуры данных ELF
15
Global offset table
Code
f(void)
Address slot
Динамическая библиотека: структуры данных ELF
16
Relocation table Global offset table
Symbol table Code
f(void)
Address slot
"var"
idx offsetaddend
Символы ELF: static
17
a.c b.c
libx.so
static int var; static int var;
f(void) g(void)
Символы ELF: default
18
libx.so
int var;
f(void)
Символы ELF: default
19
libx.so
int var;
f(void) g(void)
liby.so
Символы ELF: interposition
20
libx.so
int var;
f(void)
int var;
g(void)
liby.so
Символы ELF: hidden
21
libx.so
int var;
f(void)
int var
__attribute__
((visibility ("hidden"));
g(void)
liby.so
Метаданные патча
22
Index in patch
symbol table
Symbol address
in target
1 0x1234
2 0x5678
Метаданные патча
23
Function address
in target
Function address
in patch
0x1234 0x5678
0xABCD 0x4321
Приложение патча
24
Target process
f(void)
int var
Приложение патча
25
Target process
f(void)
int var
Patch object
Address slot
f(void)
&var
Приложение патча
26
Target process
f(void)
int var
Patch object
Address slot
f(void)
&var
JUMP
Приложение патча
27
Target process
f(void)
int var
Patch object
Address slot
f(void)
Создание патча: исходный код
28
/* Original code, in file a.c */
static int var;
static int f(void)
{
printf("%dn", var);
return var--;
}
Создание патча: исходный код
29
/* Desired code */
static int var;
static int f(void)
{
printf("%dn", var);
return var++;
}
Создание патча: исходный код
30
/* Patch code, in file patch.c */
#include <vzp.h>
VZP_FILE("a.c")
VZP_STATIC_VAR_REF(int, var);
static int f(void)
{
printf("%dn", var);
return var++;
}
Создание патча: сборка
31
# gcc -fpic -shared -O0 -g patch.c -o libpatch.so
Создание патча: сборка
32
# gcc -fpic -shared -O0 -g patch.c -o libpatch.so
# python nsbgen.py generate /path/to/binary libpatch.so
Создание патча: сборка
33
# gcc -fpic -shared -O0 -g patch.c -o libpatch.so
# python nsbgen.py generate /path/to/binary libpatch.so
# nsb -f libpatch.so -p 1234
Заключение
➢ Требуется ревью кода :)
34
Заключение
➢ Требуется ревью кода :)
➢ Используется стандартное ядро!
35
Заключение
➢ Требуется ревью кода :)
➢ Используется стандартное ядро!
➢ Бинарники собираются как обычно!
36
Заключение
➢ Требуется ревью кода :)
➢ Используется стандартное ядро!
➢ Бинарники собираются как обычно!
➢ Libcompel используется для:
➢Останова процесса
➢Перезапуска процесса
➢Загрузки кода патча
➢Source: https://github.com/xemul/criu/tree/criu-dev
37
Заключение
➢ Требуется ревью кода :)
➢ Используется стандартное ядро!
➢ Бинарники собираются как обычно!
➢ Libcompel используется для:
➢Останова процесса
➢Перезапуска процесса
➢Загрузки кода патча
➢Source: https://github.com/xemul/criu/tree/criu-dev
➢ Libunwind используется для анализа стека
➢Source: http://git.savannah.gnu.org/cgit/libunwind.git
38
Заключение
➢ Требуется ревью кода :)
➢ Используется стандартное ядро!
➢ Бинарники собираются как обычно!
➢ Libcompel используется для:
➢Останова процесса
➢Перезапуска процесса
➢Загрузки кода патча
➢Source: https://github.com/xemul/criu/tree/criu-dev
➢ Libunwind используется для анализа стека
➢Source: http://git.savannah.gnu.org/cgit/libunwind.git
➢ Source: https://github.com/virtuozzo/nsb
39
Thanks for your attention.
Q&A. Thanks!

Как мы учились чинить самолеты в воздухе / Евгений Коломеец (Virtuozzo)