Русский
Русский
English
Статистика
Реклама

Qemu

Альтернативы VirtualBox для любителей приватности и свободы. Гипервизоры и менеджеры виртуальных машин. Часть I

03.01.2021 22:21:10 | Автор: admin

Приветствую читателей.

Меня зовут Андреас, давно веду видеоблог (SunAndreas) на темы гражданской информационной безопасности и сегодня о том, какими гипервизорами и менеджерами виртуальных машин лучше пользоваться особым любителям приватности и свободы. Если вы не знакомы с иными материалами моего YouTube-канала, рекомендую ознакомиться с его содержимым.

В сети крайне мало информации по более этичным аналогам инструментов виртуализации, а также имеющаяся информация не достаточная и не вполне доступная для широкого круга лиц. Сегодняшний теоретический выпуск больше для рядовых пользователей и входит в цикл видео на моём канале о виртуализации, ожидайте и последующие практические части об установке, настройке и использовании более правоверной виртуализации, чем VirtualBox. В практических частях также поговорим и о работе виртуальных машин с VPN и TOR. А сегодня пока что немного теории.

В сети только и пестрит VirtualBox, да, VirtualBox. Конечно, это очень простой и удобный вариант для совсем новичков, а также и для пользователей виндоус, которые больше ценят удобство и популярность, но сознательным людям, использующим GNU/Linux системы, настоятельно рекомендую перейти, если вы всё ещё этого не сделали, на более этичные аналоги, после того как, вроде пару лет назад, Oracle, пересмотрела немного свои позиции в отношении VirtualBox.

Ранее, более 4 лет назад, когда ещё на канале публиковалось почти легендарное видео с названием SunbooK, тогда VirtualBox был большей мере открытым и свободным проектом, не было с моей стороны к нему никакой пренебрежительности, но ныне уже лучше всё же использовать иные гипервизоры, тем более, что они позволяют легко импортировать виртуальные диски, то есть по сути виртуальные машины, которые ранее были созданы с помощью VirtualBox. Таким образом переезд с VirtualBox на иные решения может обойтись вам даже без потери данных.

Перечисленные далее мной примеры графических оболочек, то есть программы-менеджеры, используют такие инструменты виртуализации как QEMU и KVM, а уже в свою очередь с этими инструментами виртуализации работает утилита с названием libvirt, которая функционирует как демон.

Далее я перечислю графические оболочки, то есть праграммы-менеджеры виртуальных машин, с которыми и будет работать рядовой пользователь не вникая, что такое QEMU, KVM и libvirt. Продвинутый пользователь может работать и напрямую через терминал с libvirt, но я озвучу программные решения для более широкого круга лиц.

Первая графическая оболочка. Вы можете использовать программу GNOME boxes, которая по моему скромному мнению является слишком простой и не позволяет очень много реализовать, настроить и заставить работать виртуальную машину так, как хотел бы изощрённый пользователь. Но если кого-то из вас не интересуют тонкие и особо полезные настройки виртуальных машин, то может для очень простого использования вам данный вариант и подойдёт, а я не останавливаюсь на нём, полагаю, что каждый желающий сможет интуитивно разобраться с ним без сторонней помощи, и перехожу ко второму варианту.

AQEMU это более продвинутый вариант графической оболочки, но всё же не лучший, на мой взгляд. Менеджер позволяет осуществлять различные настройки, но по моему опыту немного уступает следующему менеджеру. Знайте о существовании AQEMU и фанаты тематики могут его опробывать самостоятельно, а мы двигаемся дальше.

Третий вариант. Virt-manager также, как и предыдущие менеджеры работает с QEMU и KVM через libvirt, но кроме того может работать и с иными вариантами виртуализации, не только с гипервизорами, но и с более лёгким вариантом виртуализации, а именно, с контейнерной виртуализацией,

Если вы не смотрели отдельное видео на моём канале о гипервизорах и контейнерной виртуализации, обязательно посмотрите.

Если коротко, то, как правило, контейнерная виртуализациия более лёгкая, чем у гипервизоров. Она запускает системы или отдельные программы на том же самом ядре, что и ваша хостовая, то есть первичная, основная операционная система. Но при своих плюсах и малых требованиях к вычислительным мощностям компьютера, контейнерный тип виртуализации не был нацелен изначально на то, что бы реализовывать запуски операционных систем или отдельных программ с графическим интерфейсом, среди которых привычные браузеры и прочие программы, которые имеют графический интерфейс, а не работают сугубо в консоли. Изначально уклон контейнерной виртуализации был на работу с операционными системами и программами через консоль, терминал, а не выводить и не работать с графическим окружением, оболочками.

А гипервизоры в свою очередь изначально создавались для реализации именно запусков целых операционных систем в графическом виде, в том числе различных между собой.

Возвращаясь к Virt-manager, если вы пожелаете использовать с помощью него лёгкую контейнерную виртуализацию, то я могу порекомендовать не заморачиваться и не выискивать варианты, и использовать LXC. Если же вас интересуют иные варианты, ещё раз обращаю ваш взор к изучению отдельного видео на канале, ссылка под этим видео.

Останавливаться на работе Virt-manager с контейнерной виртуализацией с помощью LXC я не буду, так как, полагаю, это будет интересно значительно меньшему числу зрителей. А если я не прав, можете оставить комментарий с пожеланием отдельно осветить это направление. Вдруг желающих окажется не мало и я ошибаюсь.

На этом завершаем теорию и в следующей публикации разберём работу с QEMU и KVM. Существует и ещё один добротный вариант, это Xen, с которым также работает virt-manager, и я уважаю этот гипервизор и регулярно использую его. О нём я рассказывал в отдельном выпуске о виртуализации. А связка QEMU и KVM является доступной, лёгкой в работе, пожалуй, чуть более удобнее для простых пользователей и не требуется при загрузке операционной системы в загрузочном GRUB-меню выбирать никакие варианты.

В последующих публикациях я покажу работу virt-manager с QEMU-KVM, которая будет понятна даже не продвинутому пользователю, что считаю особо общественно-полезным делом, тем более, что работе данного ПО даже в английском сегменте интернета не достаточно информации на мой взгляд. А в русско-язычной сфере, так я вообще не встречал ничего особо достойного по данной теме. Лишь малоценные поверхностные обзоры.

Всем развития и успехов.

Подробнее..

Проверка QEMU с помощью PVS-Studio

04.09.2020 10:14:53 | Автор: admin
image1.png

QEMU достаточно известное приложение для эмуляции. Статический анализ может помочь разработчикам таких сложных проектов, как QEMU, отлавливать ошибки на раннем этапе и в целом повысить его качество и надёжность. В этой статье будет проверен исходный код приложения QEMU на потенциальные уязвимости и ошибки с помощью инструмента статического анализа PVS-Studio.

QEMU является свободным ПО, предназначенным для эмуляции аппаратного обеспечения различных платформ. Оно позволяет запускать приложения и операционные системы на отличающихся от целевой аппаратных платформах, например, приложение, написанное для MIPS запустить для архитектуры x86. QEMU также поддерживает эмуляцию разнообразных периферийных устройств, например, видеокарты, usb и т.д. Проект достаточно сложный и достойный внимания, именно такие проекты представляют интерес для статического анализа, поэтому было решено проверить его код с помощью PVS-Studio.

Об анализе


Исходный код проекта можно получить из зеркала на github. Проект достаточно объемный и может компилироваться для различных платформ. Для более легкой проверки кода воспользуемся системой мониторинга компиляции PVS-Studio. Эта система предназначена для очень простой интеграции статического анализа практически в любую сборочную платформу. Система основана на отслеживании вызовов компилятора во время сборки и позволяет собрать всю информацию для последующего анализа файлов. Другими словами, просто запускаем сборку, PVS-Studio собирает необходимую информацию, а после запускаем анализ все просто. Подробности можно почитать по ссылке выше.

После проверки анализатор обнаружил множество потенциальных проблем. Для диагностик общего назначения (General Analysis) было получено: 1940 уровня High, 1996 уровня Medium, 9596 уровня Low. После просмотра всех предупреждений было решено остановиться на диагностиках для первого уровня достоверности (High). Таких предупреждений нашлось достаточно много (1940), но большая часть предупреждений либо однотипна, либо связана с многократным использованием подозрительного макроса. Для примера рассмотрим макрос g_new.

#define g_new(struct_type, n_structs)                        _G_NEW (struct_type, n_structs, malloc)#define _G_NEW(struct_type, n_structs, func)       \  (struct_type *) (G_GNUC_EXTENSION ({             \    gsize __n = (gsize) (n_structs);               \    gsize __s = sizeof (struct_type);              \    gpointer __p;                                  \    if (__s == 1)                                  \      __p = g_##func (__n);                        \    else if (__builtin_constant_p (__n) &&         \             (__s == 0 || __n <= G_MAXSIZE / __s)) \      __p = g_##func (__n * __s);                  \    else                                           \      __p = g_##func##_n (__n, __s);               \    __p;                                           \  }))

На каждое использование этого макроса анализатор выдает предупреждение V773 (Visibility scope of the '__p' pointer was exited without releasing the memory. A memory leak is possible). Макрос g_new определен в библиотеке glib, он использует макрос _G_NEW, а этот макрос в свою очередь использует другой макрос G_GNUC_EXTENSION, говорящий компилятору GCC пропускать предупреждения о нестандартном коде. Именно этот нестандартный код и вызывает предупреждение анализатора, обратите внимание на предпоследнюю строку. В целом же макрос является рабочим. Предупреждений этого типа нашлось 848 штук, то есть почти половина срабатываний приходится всего лишь на одно единственное место в коде.

Все эти лишние предупреждения легко убрать, используя настройки анализатора. Впрочем, конкретно данный случай, который встретился при написании статьи, является поводом нашей команды немного доработать логику анализатора для таких ситуаций.

Таким образом, большое количество предупреждений не всегда говорит о плохом качестве кода. Тем не менее есть некоторые по-настоящему подозрительные места. Что же, давайте приступим к разбору предупреждений.

Предупреждение N1

V517 The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error presence. Check lines: 2395, 2397. megasas.c 2395

#define MEGASAS_MAX_SGE 128             /* Firmware limit */....static void megasas_scsi_realize(PCIDevice *dev, Error **errp){  ....  if (s->fw_sge >= MEGASAS_MAX_SGE - MFI_PASS_FRAME_SIZE) {    ....  } else if (s->fw_sge >= 128 - MFI_PASS_FRAME_SIZE) {    ....  }  ....}

Любые использования "магических" чисел в коде всегда вызывают подозрение. Здесь два условия, и на первый взгляд они кажутся разными, но, если посмотреть значение макроса MEGASAS_MAX_SGE, то окажется, что условия дублируют друг друга. Скорее всего, здесь опечатка и вместо 128 должно стоять другое число. Конечно, это проблема всех "магических" чисел, достаточно просто опечататься при их использовании. Применение макросов и констант сильно помогает разработчику в этом случае.

Предупреждение N2

V523 The 'then' statement is equivalent to the 'else' statement. cp0_helper.c 383

target_ulong helper_mftc0_cause(CPUMIPSState *env){  ....  CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc);  if (other_tc == other->current_tc) {    tccause = other->CP0_Cause;  } else {    tccause = other->CP0_Cause;  }  ....}

В рассматриваемом коде тела then и else условного оператора идентичны. Здесь, скорее всего, copy-paste. Просто скопировали тело then ветвления, а исправить забыли. Можно предположить, что вместо объекта other необходимо было использовать env. Исправление этого подозрительного места могло бы выглядеть следующим образом:

if (other_tc == other->current_tc) {  tccause = other->CP0_Cause;} else {  tccause = env->CP0_Cause;}

Однозначно сказать, как должно быть на самом деле могут только разработчики этого кода. Еще похожее место:

  • V523 The 'then' statement is equivalent to the 'else' statement. translate.c 641

Предупреждение N3

V547 Expression 'ret < 0' is always false. qcow2-cluster.c 1557

static int handle_dependencies(....){  ....  if (end <= old_start || start >= old_end) {    ....  } else {    if (bytes == 0 && *m) {      ....      return 0;           // <= 3    }    if (bytes == 0) {      ....      return -EAGAIN;     // <= 4    }  ....  }  return 0;               // <= 5}int qcow2_alloc_cluster_offset(BlockDriverState *bs, ....){  ....  ret = handle_dependencies(bs, start, &cur_bytes, m);  if (ret == -EAGAIN) {   // <= 2    ....  } else if (ret < 0) {   // <= 1    ....  }}

Здесь анализатор обнаружил, что условие (комментарий 1) никогда не выполнится. Значение переменной ret инициализируется результатом выполнения функции handle_dependencies, эта функция возвращает только 0 или -EAGAIN (комментарии 3, 4, 5). Чуть выше, в первом условии, мы проверили значение ret на -EAGAIN (комментарий 2), поэтому результат выполнения выражения ret < 0 будет всегда ложным. Возможно, раньше функция handle_dependencies и возвращала другие значения, но потом в результате, например, рефакторинга поведение поменялось. Здесь надо просто завершить рефакторинг. Похожие срабатывания:

  • V547 Expression is always false. qcow2.c 1070
  • V547 Expression 's->state != MIGRATION_STATUS_COLO' is always false. colo.c 595
  • V547 Expression 's->metadata_entries.present & 0x20' is always false. vhdx.c 769

Предупреждение N4

V557 Array overrun is possible. The 'dwc2_glbreg_read' function processes value '[0..63]'. Inspect the third argument. Check lines: 667, 1040. hcd-dwc2.c 667

#define HSOTG_REG(x) (x)                                             // <= 5....struct DWC2State {  ....#define DWC2_GLBREG_SIZE    0x70  uint32_t glbreg[DWC2_GLBREG_SIZE / sizeof(uint32_t)];              // <= 1  ....}....static uint64_t dwc2_glbreg_read(void *ptr, hwaddr addr, int index,                                 unsigned size){  ....  val = s->glbreg[index];                                            // <= 2  ....}static uint64_t dwc2_hsotg_read(void *ptr, hwaddr addr, unsigned size){  ....  switch (addr) {    case HSOTG_REG(0x000) ... HSOTG_REG(0x0fc):                      // <= 4        val = dwc2_glbreg_read(ptr, addr,                              (addr - HSOTG_REG(0x000)) >> 2, size); // <= 3    ....  }  ....}

В этом коде есть потенциальная проблема с выходом за границу массива. В структуре DWC2State определен массив glbreg,состоящий из 28 элементов (комментарий 1). В функции dwc2_glbreg_read по индексу обращаются к нашему массиву (комментарий 2). Теперь обратите внимание, что в функцию dwc2_glbreg_read в качестве индекса передают выражение (addr HSOTG_REG(0x000)) >> 2 (комментарий 3), которое может принимать значение в диапазоне [0..63]. Для того чтобы в этом убедиться, обратите внимание на комментарии 4 и 5. Возможно, тут надо скорректировать диапазон значений из комментария 4.

Еще похожие срабатывания:

  • V557 Array overrun is possible. The 'dwc2_hreg0_read' function processes value '[0..63]'. Inspect the third argument. Check lines: 814, 1050. hcd-dwc2.c 814
  • V557 Array overrun is possible. The 'dwc2_hreg1_read' function processes value '[0..191]'. Inspect the third argument. Check lines: 927, 1053. hcd-dwc2.c 927
  • V557 Array overrun is possible. The 'dwc2_pcgreg_read' function processes value '[0..127]'. Inspect the third argument. Check lines: 1012, 1060. hcd-dwc2.c 1012

Предупреждение N5

V575 The 'strerror_s' function processes '0' elements. Inspect the second argument. commands-win32.c 1642

void qmp_guest_set_time(bool has_time, int64_t time_ns,                         Error **errp){  ....  if (GetLastError() != 0) {    strerror_s((LPTSTR) & msg_buffer, 0, errno);    ....  }}

Функция strerror_s возвращает текстовое описание кода системной ошибки. Её сигнатура выглядит так:

errno_t strerror_s( char *buf, rsize_t bufsz, errno_t errnum );

Первый параметр это указатель на буфер, куда будет скопировано текстовое описание, второй параметр размер буфера, третий код ошибки. В коде в качестве размера буфера передается 0, это явно ошибочное значение. Кстати, есть возможность узнать заранее сколько байт надо выделять: надо просто вызвать strerrorlen_s, которая возвращает длину текстового описания ошибки. Это значение можно использовать для выделения буфера достаточного размера.

Предупреждение N6

V595 The 'blen2p' pointer was utilized before it was verified against nullptr. Check lines: 103, 106. dsound_template.h 103

static int glue (    ....    DWORD *blen1p,    DWORD *blen2p,    int entire,    dsound *s    ){  ....  dolog("DirectSound returned misaligned buffer %ld %ld\n",        *blen1p, *blen2p);                         // <= 1  glue(.... p2p ? *p2p : NULL, *blen1p,                            blen2p ? *blen2p : 0); // <= 2....}

В этом коде значение аргумента blen2p сначала используется (комментарий 1), а потом проверяется на nullptr (комментарий 2). Это крайне подозрительное место выглядит так, как будто просто забыли вставить проверку перед первым использованием (комментарий 1). Как вариант исправления просто добавить проверку:

dolog("DirectSound returned misaligned buffer %ld %ld\n",      *blen1p, blen2p ? *blen2p : 0);

Тут еще возникает вопрос по поводу аргумента blen1p. Вероятно, он тоже может быть нулевым указателем, и тут тоже надо будет добавить проверку. Еще несколько подобных срабатываний:

  • V595 The 'ref' pointer was utilized before it was verified against nullptr. Check lines: 2191, 2193. uri.c 2191
  • V595 The 'cmdline' pointer was utilized before it was verified against nullptr. Check lines: 420, 425. qemu-io.c 420
  • V595 The 'dp' pointer was utilized before it was verified against nullptr. Check lines: 288, 294. onenand.c 288
  • V595 The 'omap_lcd' pointer was utilized before it was verified against nullptr. Check lines: 81, 87. omap_lcdc.c 81

Предупреждение N7

V597 The compiler could delete the 'memset' function call, which is used to flush 'op_info' object. The RtlSecureZeroMemory() function should be used to erase the private data. virtio-crypto.c 354

static void virtio_crypto_free_request(VirtIOCryptoReq *req){  if (req) {    if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) {      ....      /* Zeroize and free request data structure */      memset(op_info, 0, sizeof(*op_info) + max_len); // <= 1      g_free(op_info);    }    g_free(req);  }}

В этом фрагменте кода вызывается функция memset для объекта op_info (комментарий 1), после этого op_info сразу удаляется, то есть, другими словами, после очистки этот объект нигде больше не модифицируется. Это как раз тот самый случай, когда в процессе оптимизации компилятор может удалить вызов memset. Чтобы исключить подобное потенциальное поведение, можно воспользоваться специальными функциями, которые компилятор никогда не удаляет. См. также статью "Безопасная очистка приватных данных".

Предупреждение N8

V610 Unspecified behavior. Check the shift operator '>>'. The left operand is negative ('number' = [-32768..2147483647]). cris.c 2111

static voidprint_with_operands (const struct cris_opcode *opcodep,         unsigned int insn,         unsigned char *buffer,         bfd_vma addr,         disassemble_info *info,         const struct cris_opcode *prefix_opcodep,         unsigned int prefix_insn,         unsigned char *prefix_buffer,         bfd_boolean with_reg_prefix){  ....  int32_t number;  ....  if (signedp && number > 127)    number -= 256;            // <= 1  ....  if (signedp && number > 32767)    number -= 65536;          // <= 2  ....  unsigned int highbyte = (number >> 24) & 0xff;  ....}

Так как переменная number может иметь отрицательное значение, побитовый сдвиг вправо является неуточнённым поведением (unspecified behavior). Чтобы убедиться в том, что рассматриваемая переменная может принять отрицательное значение, обратите внимание на комментарии 1 и 2. Для устранения различий поведения вашего кода на различных платформах, таких случаев нужно не допускать.

Еще предупреждения:

  • V610 Undefined behavior. Check the shift operator '<<'. The left operand is negative ('(hclk_div 1)' = [-1..15]). aspeed_smc.c 1041
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(target_long) 1' is negative. exec-vary.c 99
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand is negative ('hex2nib(words[3][i * 2 + 2])' = [-1..15]). qtest.c 561

Также есть несколько предупреждений такого же типа, только в качестве левого операнда выступает -1.

V610 Undefined behavior. Check the shift operator '<<'. The left operand '-1' is negative. hppa.c 2702

int print_insn_hppa (bfd_vma memaddr, disassemble_info *info){  ....  disp = (-1 << 10) | imm10;  ....}

Другие подобные предупреждения:

  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '-1' is negative. hppa.c 2718
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '-0x8000' is negative. fmopl.c 1022
  • V610 Undefined behavior. Check the shift operator '<<'. The left operand '(intptr_t) 1' is negative. sve_helper.c 889

Предупреждение N9

V616 The 'TIMER_NONE' named constant with the value of 0 is used in the bitwise operation. sys_helper.c 179

#define HELPER(name) ....enum {  TIMER_NONE = (0 << 30),        // <= 1  ....}void HELPER(mtspr)(CPUOpenRISCState *env, ....){  ....  if (env->ttmr & TIMER_NONE) {  // <= 2    ....  }}

Можно легко убедиться, что значение макроса TIMER_NONE равно нулю (комментарий 1). Далее этот макрос используется в побитовой операции, результат которой всегда будет 0. Как итог, тело условного оператора if (env->ttmr & TIMER_NONE) никогда не выполнится.

Предупреждение N10

V629 Consider inspecting the 'n << 9' expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type. qemu-img.c 1839

#define BDRV_SECTOR_BITS   9static int coroutine_fn convert_co_read(ImgConvertState *s,                   int64_t sector_num, int nb_sectors, uint8_t *buf){  uint64_t single_read_until = 0;  int n;  ....  while (nb_sectors > 0) {    ....    uint64_t offset;    ....    single_read_until = offset + (n << BDRV_SECTOR_BITS);    ....  }  ....}

В этом фрагменте кода над переменной n, имеющей 32-битный знаковый тип, выполняется операция сдвига, потом этот 32-битный знаковый результат расширяется до 64-битного знакового типа, и далее, как беззнаковый тип, складывается с беззнаковой 64-битной переменной offset. Предположим, что на момент выполнения выражения переменная n имеет некоторые значимые старшие 9 бит. Мы выполняем операцию сдвига на 9 разрядов (BDRV_SECTOR_BITS), а это, в свою очередь, является неопределенным поведением, тогда в качестве результата мы можем получить выставленный бит в старшем разряде. Напомним, что этот бит в знаковом типе отвечает за знак, то есть результат может стать отрицательным. Так как переменная n знакового типа, то при расширении будет учтен знак. Далее результат складывается с переменной offset. Из этих рассуждений нетрудно увидеть, что результат выполнения выражения может отличаться от предполагаемого. Одним из возможных вариантов решения является замена типа переменной n на 64-битный беззнаковый тип, то есть на uint64_t.

Вот еще похожие срабатывания:

  • V629 Consider inspecting the '1 << refcount_order' expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type. qcow2.c 3204
  • V629 Consider inspecting the 's->cluster_size << 3' expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type. qcow2-bitmap.c 283
  • V629 Consider inspecting the 'i << s->cluster_bits' expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type. qcow2-cluster.c 983
  • V629 Consider inspecting the expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type. vhdx.c 1145
  • V629 Consider inspecting the 'delta << 2' expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type. mips.c 4341

Предупреждение N11

V634 The priority of the '*' operation is higher than that of the '<<' operation. It's possible that parentheses should be used in the expression. nand.c 310

static void nand_command(NANDFlashState *s){  ....  s->addr &= (1ull << s->addrlen * 8) - 1;  ....}

Тут просто подозрительное место. Непонятно, что программист хотел сделать вначале: сдвиг или умножение. Даже если здесь нет ошибки, все равно надо еще раз взглянуть на код и правильно расставить скобочки. Тут как раз одно из таких мест, которые должны смотреть разработчики, чтобы убедиться в корректности своего алгоритма. Другие такие места:

  • V634 The priority of the '*' operation is higher than that of the '<<' operation. It's possible that parentheses should be used in the expression. exynos4210_mct.c 449
  • V634 The priority of the '*' operation is higher than that of the '<<' operation. It's possible that parentheses should be used in the expression. exynos4210_mct.c 1235
  • V634 The priority of the '*' operation is higher than that of the '<<' operation. It's possible that parentheses should be used in the expression. exynos4210_mct.c 1264

Предупреждение N12

V646 Consider inspecting the application's logic. It's possible that 'else' keyword is missing. pl181.c 400

static void pl181_write(void *opaque, hwaddr offset,                        uint64_t value, unsigned size){  ....  if (s->cmd & PL181_CMD_ENABLE) {    if (s->cmd & PL181_CMD_INTERRUPT) {      ....    } if (s->cmd & PL181_CMD_PENDING) { // <= else if      ....    } else {      ....    }    ....  }  ....}

В этом коде, судя по форматированию, прямо напрашивается использование else if вместо if. Возможно, здесь забыли дописать else. Тогда вариант исправления может быть такой:

} else if (s->cmd & PL181_CMD_PENDING) { // <= else if

Однако есть вероятность, что с этим кодом все в порядке, и тут некорректное форматирование текста программы, которое сбивает с толку. Тогда код мог бы выглядеть так:

if (s->cmd & PL181_CMD_INTERRUPT) {  ....}if (s->cmd & PL181_CMD_PENDING) { // <= if  ....} else {  ....}

Предупреждение N13

V773 The function was exited without releasing the 'rule' pointer. A memory leak is possible. blkdebug.c 218

static int add_rule(void *opaque, QemuOpts *opts, Error **errp){  ....  struct BlkdebugRule *rule;  ....  rule = g_malloc0(sizeof(*rule));                   // <= 1  ....  if (local_error) {    error_propagate(errp, local_error);    return -1;                                       // <= 2  }  ....  /* Add the rule */  QLIST_INSERT_HEAD(&s->rules[event], rule, next);   // <= 3  ....}

В данном коде выделяется объект rule (комментарий 1) и добавляется в список для последующего использования (комментарий 3), но в случае ошибки происходит возврат из функции без удаления ранее созданного объекта rule (комментарий 2). Здесь надо просто правильно обработать ошибку: удалить ранее созданный объект, иначе будет утечка памяти.

Предупреждение N14

V781 The value of the 'ix' index is checked after it was used. Perhaps there is a mistake in program logic. uri.c 2110

char *uri_resolve_relative(const char *uri, const char *base){  ....  ix = pos;  if ((ref->path[ix] == '/') && (ix > 0)) {  ....}

Здесь анализатор обнаружил потенциальный выход за границу массива. Сначала читается элемент массива ref->path по индексу ix, а потом ix проверяется на корректность (ix > 0). Правильным решением тут будет поменять эти действия местами:

if ((ix > 0) && (ref->path[ix] == '/')) {

Таких мест нашлось несколько:

  • V781 The value of the 'ix' index is checked after it was used. Perhaps there is a mistake in program logic. uri.c 2112
  • V781 The value of the 'offset' index is checked after it was used. Perhaps there is a mistake in program logic. keymaps.c 125
  • V781 The value of the 'quality' variable is checked after it was used. Perhaps there is a mistake in program logic. Check lines: 326, 335. vnc-enc-tight.c 326
  • V781 The value of the 'i' index is checked after it was used. Perhaps there is a mistake in program logic. mem_helper.c 1929

Предупреждение N15

V784 The size of the bit mask is less than the size of the first operand. This will cause the loss of higher bits. cadence_gem.c 1486

typedef struct CadenceGEMState {  ....  uint32_t regs_ro[CADENCE_GEM_MAXREG];}....static void gem_write(void *opaque, hwaddr offset, uint64_t val,        unsigned size){  ....  val &= ~(s->regs_ro[offset]);  ....}

В этом коде выполняется побитовая операция с объектами разных типов. Левый операнд это аргумент val, имеющий 64-битный беззнаковый тип. В качестве правого операнда выступает полученное значение элемента массива s->regs_ro по индексу offset, имеющий 32-битный беззнаковый тип. Результат операции в правой части (~(s->regs_ro[offset])) является 32-битным беззнаковым типом, и перед побитовым умножением он расширится до 64-битного типа нулями, то есть после вычисления всего выражения обнулятся все старшие биты переменной val. Такие места всегда выглядят подозрительными. Тут можно только порекомендовать разработчикам еще раз пересмотреть этот код. Еще похожее:

  • V784 The size of the bit mask is less than the size of the first operand. This will cause the loss of higher bits. xlnx-zynq-devcfg.c 199
  • V784 The size of the bit mask is less than the size of the first operand. This will cause the loss of higher bits. soc_dma.c 214
  • V784 The size of the bit mask is less than the size of the first operand. This will cause the loss of higher bits. fpu_helper.c 418

Предупреждение N16

V1046 Unsafe usage of the 'bool' and 'unsigned int' types together in the operation '&='. helper.c 10821

static inline uint32_t extract32(uint32_t value, int start, int length);....static ARMVAParameters aa32_va_parameters(CPUARMState *env, uint32_t va,                                          ARMMMUIdx mmu_idx){  ....  bool epd, hpd;  ....  hpd &= extract32(tcr, 6, 1);}

В этом фрагменте кода происходит операция побитового И над переменной hpd, имеющей тип bool, и результатом выполнения функции extract32, имеющим тип uint32_t. Так как битовое значение булевой переменной может быть только 0 или 1, то результат выражения будет всегда false, если младший бит, возвращаемый функцийе extract32, равен нулю. Давайте рассмотрим это на примере. Предположим, что значение hpd равно true, а функция вернула значение 2, то есть в двоичном представлении операция будет выглядеть так 01 & 10 = 0, а результат выражения будет равен false. Скорее всего, программист хотел выставлять значение true, если функция возвращает что-то отличное от нуля. По всей видимости, надо исправить код так, чтобы результат выполнения функции приводился к типу bool, например, так:

hpd = hpd && (bool)extract32(tcr, 6, 1);

Заключение


Как видно, анализатор нашел много подозрительных мест. Возможно, найденные потенциальные проблемы пока никак себя не проявляют, но их наличие не может не тревожить, так как они способны выстрелить в самый неожиданный момент. Просмотреть все подозрительные места заранее и исправить лучше, чем чинить бесконечный поток багов. Очевидно, что для сложных проектов, подобных этому, статический анализ может принести ощутимую пользу, особенно если организовать регулярную проверку проекта. Если Вы хотите попробовать PVS-Studio для своего проекта, вы можете скачать анализатор и получить бесплатный триальный ключ на этой странице.


Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Evgeniy Ovsannikov. Checking QEMU using PVS-Studio.
Подробнее..

Язык тестовых сценариев Testo Lang простая автоматизация сложных тестов

12.10.2020 08:12:38 | Автор: admin

Картинка для привлечения внимания


Если Вы разрабатываете более-менее сложный программный продукт, то Вам должна быть знакома ситуация, когда системные (end-to-end) тесты по тем или иным причинам автоматизировать не удаётся. На это могут быть разные причины, я приведу несколько примеров:


  • У приложения нет и не может быть API, за которое можно зацепиться, по соображениям безопасности;
  • Приходится поддерживать legacy-проект, про автоматизацию тестирования которого никто никогда не задумывался;
  • Во время тестирования задействуется сторонний продукт, например антивирус;
  • Необходимо проверить работоспособность продукта на большом количестве различных целевых платформ;
  • Тестовый стенд представляет собой сложную гетерогенную систему, включающую в себя промежуточное сетевое оборудование.

Эти и многие другие ситуации приводят к худшему кошмару любого разработчика ручному тестированию. Самое неприятное заключается в том, что нельзя провести тестирование один раз и забыть о нём. Нет, нам приходится перед каждым релизом (а может и чаще) раскатывать виртуалки, устанавливать туда тестируемое приложение и тыкать по кнопкам снова и снова, чтобы убедиться, что мы не словили регрессию.


Если Вы ищете решение этой проблемы то прошу под кат.


Любой тест, выполняемый на виртуальной машине, можно автоматизировать с помощью последовательности простых действий, таких как движение мышкой или нажатие клавиш на клавиатуре. Это именно те действия, которые совершает тестировщик во время ручного тестирования. Такие тесты могли бы выглядеть, грубо говоря, следующим образом:


кликнуть_мышкой_на_кнопке_с_надписью "Сохранить"напечатать_на_клавиатуре "Hello world"дождаться_надписи_на_экране "Готово"

При этом неважно, что именно вы тестируете: XAML-приложение, Qt-приложение, Electron-приложение, веб-страницу или вообще консольное приложение. Вы кликаете по экрану виртуальной машины и набираете текст на клавиатуре, а как приложение устроено внутри это Вас уже совершенно не волнует. Удобно? Конечно!


Одна только загвоздка довольно трудно понять, где на экране виртуалки находится кнопка "Сохранить", и есть ли на экране надпись "Готово". Я думаю, это одна из причин, почему мы не видим на рынке переизбытка инструментов, работающих по описанному принципу.


Однако, в последнее время технологии компьютерного зрения шагнули далеко вперёд. Искусственные нейронные сети успешно справляются с такими сложными задачами, как, например, управление автомобилем. Неужели они не справятся с такой заведомо более простой задачей, как обнаружение элементов GUI на экране виртуальной машины?


Как вы могли уже догадаться, нейросетям такая задача вполне по плечу. Вы можете сами в этом убедится, попробовав в действии новый инструмент для автоматизации системных тестов интерпретатор тестовых сценариев Testo. Интерпретатор Testo позволяет выполнять сценарии, написанные на специализированном языке тестовых сценариев Testo Lang, которые выглядят примерно следующим образом:


mouse click "Сохранить"type "Hello world"wait "Готово"

Это всё, что Вам надо написать на языке Testo Lang чтобы:


  1. Кликнуть на надпись на экране "Сохранить";
  2. Напечатать на клавиатуре "Hello world";
  3. Дождаться появления на экране строки "Готово".

Однако, мне бы не хотелось, чтобы у Вас сложилось ложное впечатление, будто бы Testo это какой-то аналог AutoIt или Sikuli. Нет, это не просто инструмент для автоматизации чего-то-там, это инструмент, заточенный именно под автоматизацию системных тестов. Testo берёт под свой контроль значительное количество задач, которые обычно выполняет тестировщик: определение того, какие тесты пора прогнать заново, подготовку виртуального стенда, составление отчёта о том, какие тесты свалились и в какой момент, и так далее.


Так, с этого момента поподробнее



Итак, мы в Вами говорим именно о системных (end-to-end) тестах. Системные тесты предполагают, что Вы тестируете программу не саму по себе в вакууме, а помещаете её в конкретное окружение и смотрите, как она с этим окружением справится. Под окружением может пониматься что угодно: и версия ОС, и наличие/отсутствие каких-то приложений/драйверов, и взаимодействие по сети, и соединение с Интернетом, и недостаток дискового пространства/оперативной памяти Да и много чего ещё.


Самый удобный способ создать такое окружение для программы это установить тестируемую программу внутрь виртуальной машины. Но виртуальную машину необходимо для начала создать и установить на неё операционную систему. Давайте посмотрим, как с этой задачей справляется платформа Testo. Мы предусмотрели специальные конструкции в языке тестовых сценариев Testo Lang, предназначенные для создания элементов виртуальной инфраструктуры Ваших стендов. Например, следующий сниппет объявляет "пустую" виртуалку:


machine my_super_vm {    ram: 2Gb    cpus: 2    iso: "ubuntu_server.iso"    disk main: {        size: 5Gb    }}

Эта конструкция создаёт виртуальную машину с 2Гб оперативной памяти, 2 ядрами процессора и 5Гб дискового пространства. При запуске такой виртуалки, начнётся процесс установки операционной системы из образа ubuntu_server.iso.


Это может быть несколько непривычно, но мы рассматриваем процесс установки операционной системы как ещё один тест, наравне с теми тестами, в которых проверяется собственно работоспособность Вашей программы. Это утверждение обретёт бОльший смысл, если мы на секунду представим, что мы разрабатываем не программу, а операционную систему. Может быть это какая-то специализированная система, например Alt Linux, а может быть мы разрабатываем игрушечную операционную систему just for fun. В любом случае, тестировать её как-то надо, а платформа Testo подходит для этой цели как нельзя лучше, потому что для неё нет никакой разницы, что мы тестируем: операционную систему или программу.


Так а что же делать с пустой виртуалкой? В качестве примера давайте посмотрим, как мог бы выглядеть тест, написанный на языке Testo Lang и выполняющий установку операционной системы:


test my_super_test {    my_super_vm {        start        wait "Language"        press Enter        wait "Install Ubuntu Server"        press Enter        wait "Choose the language"        press Enter        # И так далее        ...    }}

Здесь мы видим новую конструкцию языка, которая объявляет тест my_super_test. В этом тесте учавствует всего одна виртуалка my_super_vm. Тест начинается с включения виртуальной машины. Затем мы дожидаемся, когда на экране появится надпись "Language" и нажимаем клавишу Enter. Собственно, весь тест будет заключаться в последовательности таких действий: ждём наступления события, затем печатаем что-то на клавиатуре.


Разумеется, далеко не всегда хочется заморачиваться с установкой ОС и её первичной настройкой. Поэтому мы предусмотрели возможность импорта диска от другой виртуальной машины:


machine my_super_vm {    ram: 2Gb    cpus: 2    disk main: {        source: "prepared_vm.qcow2"    }}

То есть вы можете вручную подготовить виртуальную машину, установить туда ОС, установить дополнительные программы, выполнить какие-то настройки, например, отключить фаервол, а затем получившийся диск от вручную созданной виртуалки использовать как начальное состояние для виртуалок из автотестов.


Подготовка виртуалки и установка ОС это конечно всё очень хорошо, но тесты, тесты-то на мою программу где? Хорошо, давайте представим, что мы хотим протестировать инсталлятор нашей супер-программы. Представим также, что мы уже вручную подготовили виртуальную машину с Windows 10 на борту. Для простоты примера предположим, что инсталлятор нашей супер-программы уже скопирован на рабочий стол этой виртуалки. Тогда автотест на установку программы будет выглядеть следующим образом:


machine my_win_vm {    ram: 2Gb    cpus: 2    disk main: {        source: "my_windows_10.qcow2"    }}test my_installer_test {    my_win_vm {        # Запустим виртуальную машину        start        # Дождёмся появления рабочего стола        wait "Корзина"        mouse dclick "my_super_installator"        wait "Добро пожаловать"        mouse click "Далее"        wait "Выберите путь установки"        mouse click "Продолжить"        wait "Успешно" timeout 10m        mouse click "Закрыть"    }}

Правда, просто? А мы только разогреваемся ...


Что за wait такой и как он работает?



Сделаем мини перерыв и поговорим пару минут о том, как это вообще работает. Тестовые сценарии состоят по большей части из двух вещей:


  1. Воздействие на виртуальную машину (mouse move/click, type, press, start/stop, plug flash и много чего ещё);
  2. Анализ происходящего на экране (wait).

Так вот, это действие wait и является основным видом визуальных проверок в языке Testo Lang. Действие wait дожидается появления на экране определённых объектов и событий в течение заданного таймаута (по умолчанию одна минута). И если событие не наступило генерируется ошибка (прямо как человек, который ждёт надписи "Успешно" пока у него не кончится, наконец, терпение).


Если мы говорим про поиск текста на экране виртуалки (то бишь на скриншоте), то обычно для этих целей используют какую-нибудь OCR (Optical Character Recognition) систему (например, Tesseract). Однако, это не совсем верный подход. Дело в том, что OCR-системы строятся исходя из двух постулатов:


  • предполагается, что мы ничего не знаем о том, какой текст находится на изображении;
  • задача системы извлечь из изображения как можно больше информации.

В случае автотестов мы имеем совершенно иную ситуацию:


  • мы знаем, какой текст должен находиться на изображении;
  • задача системы намного проще требуется сказать, присутствует ли заданный текст на изображении, и, если да то где он находится.

В платформе Testo для реализации этой задачи мы использовали нейросети. На вход нейросетям мы подаём сразу две вещи: скриншот экрана виртуалки и искомый текст. От нейросети требуется сказать только, есть ли что-то похожее на изображении или нет. Например, если мы ищём слово "Hopa", то нам вполне подойдёт как слово, написанное кириллицей, так и слово, написанное латиницей, потому что выглядят они совершенно одинаково.


Такой подход к визуальным проверкам позволил нам добиться приемлемых результатов по точности детекта теста и, в то же время, получить хорошую скорость работы нейросетей даже на CPU. Разумеется, мы не собираемся останавливаться на достигнутом и будем и дальше совершенствовать точность нейросетей, а также добавим возможность нечёткого поиска по картинкам/иконкам.


На одном wait далеко не уедешь


Писать длинные тесты с помощью wait + click довольно муторно, особенно, если нет автоматического рекодера тестов. Тесты на основе визуальных проверок это скорее крайний вариант, когда нет другой возможности протестировать приложение или настроить тестовое окружение. Обычно всё же существует возможность выполнить какие-то проверки путём запуска процессов на гостевой системе, например с помощью bash-скриптов.


Как раз на этот случай мы предусмотрели специальную конструкцию в языке Testo Lang. Для её работы на виртуальной машине должны быть установлены дополнения для гостевых систем, которые постовляются вместе с интерпретатором Testo. После установки гостевых дополнений становится возможным писать такие тесты:


test my_super_test  {        my_super_vm {            exec bash "echo Hello world from bash"            exec python """                print("Hello from python")            """        }}

Если какая-либо команда из bash-скрипта завершится с ошибкой, то и весь тест будет считаться проваленным. Аналогично, если python-скрипт вернёт не ноль, то выполнение теста сразу же завершится.


На самом деле довольно многие тестовые сценарии выглядят следующим образом. Сначала с помощью wait + click на гостевую систему устанавливаются дополнения, а затем проверки сводятся к запуску процессов на виртуалке. Но при этом ничто не мешает в любой момент вернуться к визуальным проверкам. Тут всё зависит от Вас как Вам удобнее, так и делайте.


Кроме того, гостевые дополнения позволяют легко копировать файлы между виртуалкой и хостом:


test copy_demo {        my_super_vm {            copyto "/file/on/host" "/file/on/guest"            copyfrom "/file/on/guest" "/file/on/host"        }}

Да зачем же придумывать целый язык?



Мне кажется, многие читатели сейчас думают: "Ребят, серьёзно? Целый язык? Зачем? Ну напишите Вы библиотеку для питона или для чего ещё. Все разумные люди так делают".


На самом деле, причин для создания собственного языка у нас было много. Вот лишь несколько из них:


  1. Мы бы хотели, чтобы у нашего языка был минимальный порог вхождения для людей, не знакомых с программированием;
  2. Мы бы хотели избавиться от лишней мишуры, присущей языкам общего назначения, оставив только то, что требуется для автотестов;
  3. Некоторые фичи, которые мы запилили в Testo-lang, просто так не воткнешь в библиотеку для Python!

Например, Testo поддерживает кеширование тестов, благодаря чему тесты можно прогонять инкрементально только в случае, когда это необходимо (аналогично инкрементальной компиляции программ).


Допустим, у Вас есть такой тест:


test run_installator {        my_super_vm {            copyto "/path/on/host.msi" "C:\\Users\\Testo\\Desktop\\setup.msi"            mouse dclick "setup"            ...        }}

Допустим, Вы его запустили, и он прогнался успешно. Если Вы тут же запустите его ещё раз он отработает мгновенно! Так а действительно, зачем прогонять тест ещё раз, если:


  1. Сам тест не менялся;
  2. Сборка Вашего инсталлятора не менялась.

А вот как только Вы соберёте новую сборку своего инсталлятора и запустите тест ещё раз то он запустится заново! Testo отслеживает все внешние файлы, участвующие в тестах, и как только в них что-то меняется, кеш соответствующих тестов сбрасывается. И да, этот механизм полностью прозрачный Вам не надо писать ничего для того, чтобы это работало.


Ух, круто. А что ещё умеет Testo?



Одна виртуалка в тестах это скучно. Самое веселье начинается, когда виртуалок становится много, и они начинают взаимодействовать между собой. В тестовых сценариях Вы можете создавать сколько угодно виртуалок и соединять их сетями (можно и доступ в Интернет добавить):


# Сеть для соединения двух виртуальных машинnetwork net1 {    mode: "internal"}# Сеть для доступа в Итнернетnetwork internet {    mode: "nat"}machine my_super_client {    ...    nic server_side: {        attached_to: "net1"    }    nic internet: {        attached_to: "internet"    }}machine my_super_server {    ...    nic client_side: {        attached_to: "net1"    }}

Хотите добавить в стенд флешку? Нет проблем, пара строк и у Вас есть виртуальная флешка (можно даже скопировать на неё что-нибудь с хоста)


flash my_super_flash {    fs: ntfs    size: 2048Mb    #Папка с хоста, которую надо скопировать    folder: "/path/on/host"}

Хотите написать реально много тестов? Нет проблем, организуйте их в иерархию! Давайте для большей конкретики рассмотрим такой набор тестов:


  1. Установка ОС;
  2. Установка гостевых дополнений;
  3. Копирование и установка тестируемой программы на виртуалку;
  4. Тестирование фичи 1;
  5. Тестирование фичи 2.

Очевидно, что каждый последующий тест можно запускать только после того, как успешно отработает предыдущий (кроме последних двух, их можно запускать независимо друг от друга). Поэтому мы можем выстроить такое дерево тестов:



На языке Testo Lang это будет выглядеть не сильно сложнее, чем на рисунке:


test install_os {    ...}test install_guest_additions: install_os {    ...}test install_app: install_guest_additions {    ...}test test_feature_1: install_app {    ...}test test_feature_2: install_app {    ...}

При первом запуске тестов, конечно, все тесты прогонятся от начала до конца. Допустим, при этом запуске не произошло никаких ошибок. В этом случае Testo запомнит, что все эти тесты завершились успешно и, соответственно, закешированы:



Если прямо сейчас заново запустить тесты, то они вовсе не прогонятся, так как ничего с момента последнего запуска и не поменялось. Но как только Вы соберёте новый билд своей тестирумой программы, Testo это отловит и инвалидирует кеш 3, 4 и 5 тестов:



При этом тест с установкой ОС и гостевых дополнений останутся закешированными в них ведь ничего не поменялось, так ведь? ОС та же самая, гостевые дополнения и текст тестового сценария тоже. Поэтому эти тесты прогоняться заново не будут. Вместо этого Testo откатит виртуалки, которые учавствуют в тестах, к тому состоянию, в котором они пребывали в конце теста install_guest_additions.


Простой, но вполне реальный пример


Возможностей Testo может хватить на десяток статей, поэтому нам бы не хотелось сейчас Вам рассказывать про все из них. Давайте лучше притормозим и рассмотрим базовый, но реальный пример с автоматизацией тестирования простенького самописного standalone-приложения MySuperApp.


Это приложение написано на С++ с использованием библиотеки ImGui, у него нет никаких хуков для автоматизации тестирования, но мы всё равно очень хотим на каждую сборку проверять, что оно успешно запускается на Windows 10 и высвечивает нам окошко с надписью "MySuperApp is working!".


Что ж, для начала нам понадобится виртуалка. Для этого примера мы создадим виртуалку на основе уже существующей, вручную подготовленной виртуалки, на которую мы заранее установили Windows 10:


machine my_vm {    cpus: 2    ram: 4Gb    disk main: {        source: "${QEMU_DISK_DIR}/win10.qcow2"    }}

А как нам скопировать на виртуалку сборку с нашей программой? Давайте для этого воспользуемся флешкой!


flash my_super_flash {    fs: "ntfs"    size: 16Mb    folder: "./dist"}

Сборку с нашей программой поместим в каталог "./dist", а Testo позаботится о том, чтобы она оказалась на флешке.


Ну а теперь пишем сам тест!


test launch_my_simple_app {    my_vm {        ...    }}

Так, стоп, а с чего начать? Да всё просто просто записывайте все действия, которые Вы бы проделывали вручную! Для начала виртуалку надо включить.


start

Окей, а дальше? Дальше надо дождаться появления рабочего стола, конечно же:


wait "Recycle Bin" timeout 10m


Вставляем флешку


plug flash my_super_flash


Кликаем по надписи "USB Drive (E:)"


mouse click "USB Drive (E:)"


Открываем файловый менеджер:


mouse click "Open folder to view files"


Дважды кликаем по нашему приложению:


mouse dclick "MySuperApp"


Как же нам понять, что наше приложение успешно запустилось? Ну, мы знаем, что наше приложение при запуске должно высвечивать надпись "hello world". Поэтому если такая надпись появилась на экране, то это с большой долей вероятности свидетельствует о том, что всё хорошо. Это и будет наша основная проверка в тесте:


wait "hello world"

В конце теста не забываем вытащить флешку, и в сумме у нас получается такой скрипт:


test launch_my_simple_app {    my_vm {        start        wait "Recycle Bin" timeout 10m        plug flash my_super_flash        mouse click "USB Drive (E:)"        mouse click "Open folder to view files"        mouse dclick "MySuperApp"        wait "hello world"        unplug flash my_super_flash    }}

Вот собственно и всё, наш первый тест готов. А как его запустить? Да тоже ничего сложного, главное указать путь QEMU_DISK_DIR:


sudo testo run my_script.testo --param QEMU_DISK_DIR /var/lib/libvirt/qemu/images

Мы подготовили небольшой видеоролик, на котором выполняется запуск этого теста:



А теперь, напоследок, давайте попробуем симитировать ситуацию, когда в нашу программу закралась ошибка.


Например, по какому-то нелепому стечению обстоятельств, мы собрали MySuperApp с динамической линковкой стандартной библиотеки С++ вместо статической. Если программа собрана таким образом, то для её работы на гостевой системе должен быть установлен пакет Microsoft Visual C++ Redistributable. А мы разрабатываем standalone-приложение, которое не должно иметь никаких зависимостей. У разработчика на хостовой системе Microsoft Visual C++ Redistributable конечно же установлен, поэтому такую ошибку легко не заметить.


Итак, мы подкладываем в каталог ./dist новую сборку нашего приложения и запускаем тесты заново. Вот что мы увидим:



При этом в выводе интерпретатора Testo будет указано, какой тест свалился и в какой именно строчке тестового сценария это произошло:



Тест свалился, ошибка выловлена!


Итоги


Системные тесты это всегда непросто, но всегда очень ценно. Мы надеемся, что с Testo Вам будет максимально комфортно тестировать свои программы в самых разных условиях, и Вы сможете спать спокойно, зная, что теперь у заказчика Ваша программа будет себя вести так, как и задумывалось.


Скачать Testo абсолютно бесплатно без регистрации и СМС, а также ознакомиться с документацией можно у нас на сайте https://testo-lang.ru


Посмотреть больше примеров можно на youtube-канале https://www.youtube.com/channel/UC4voSBtFRjRE4V1gzMZoZuA

Подробнее..

Я автоматизировал тестирование Dr. Web. А сможете ли вы?

15.10.2020 14:05:55 | Автор: admin


Я никогда не пользовался Dr. Web. Я понятия не имею, как он устроен. Но это не помешало мне написать для него ряд автотестов (и лишь лень не позволила мне написать ещё сотню других):


  1. Тест на установку Dr. Web;
  2. Тест на ограничение доступа к съемным устройствам (флешкам);
  3. Тест на разграничение доступа к каталогу между программами;
  4. Тест на разграничение доступа к каталогу между пользователями системы (родительский контроль).

Такие и многие другие тесты можно клепать как горячие пирожки, и не только применительно к Dr. Web, и не только применительно к антивирусам. В этой статье я расскажу, как это сделать.


Подготовка


Для тестов нам понадобится виртуалка с Windows на борту. Я подготовил её вручную, выполнив на ней следующие манипуляции:


  1. Собственно, установил Windows 10 Pro x64;
  2. Во время установки создал основного пользователя "testo" в паролем "1111";
  3. Включил автологин для этого пользователя;

Для автоматизации тестов я буду использовать платформу Testo. Что это такое и как этим пользоваться можно почитать здесь. Нам же сейчас требуется импортировать готовую виртуалку в автотесты. Сделать это очень просто:



Здесь предполагается, что /path/to/win10.qcow2 это путь к диску той виртуалки, которую я подготовил вручную. На этом подготовка заканчивается, и начинается экшен.


Тест 1 Устанавливаем Dr. Web!


Для начала надо решить вопрос с переносом дистрибутива Dr. Web на виртуальную машину. Сделать это можно (например) с помощью флешки:



Всё, что нам надо сделать это положить установщик Dr. Web в папочку ${DR_WEB_DIR} (точное значение этого параметра мы будем задавать при запуске testo). А Testo само позаботится о том, чтобы этот инсталлятор оказался на флешке.


Теперь можем приступить, собственно, к написанию теста. Пока что начнём тест с простых вещей: включим виртуалку (после создания она будет выключена), дождёмся появления рабочего стола, включим флешку и откроем её содержимое через проводник:



Скриншот на момент окончания сценария


Можно, конечно, запустить инсталлятор прямо отсюда, из самой флешки. Но мы лучше сделаем всё по-честному мы скопируем инсталлятор на рабочий стол и запустим инсталлятор оттуда. Как же нам скопировать файл? А как бы это сделал человек?



Скриншот, на котором ещё происходит копирование файла


Всё, копирование успешно завершено! Теперь можно закрыть окно с флешкой и вытащить её:



Скриншот после закрытия проводника


Теперь, когда инсталлятор оказался на рабочем столе, нам нужно дважды кликнуть на него чтобы запустить процесс установки. А сама установка сводится к простому прокликиванию кнопок и галочек и не представляет большого интереса:



Скриншот на момент окончания установки


Завершаем наш тест перезагрузкой. И в конце не забудем проверить, что после перезагрузки на рабочем столе появилась иконка с Dr. Web:



Скриншот после перезагрузки


Отличная работа! Мы автоматизировали установку антивируса Dr. Web! Давайте немного передохнём и посмотрим, как это выглядит в динамике:



Переходим к тестированию фич.


Тест 2 Ограничение доступа к флешкам


Первая фича по списку ограничение доступа к флешкам. Для этого спланируем довольно прямолинейный тест:


  1. Попробуем вставить флешку и создать там пустой файл должно получиться. Вытащим флешку;
  2. Включим блокировку съемных устройств в Dr. Web Security Center;
  3. Ещё раз вставим флешку и попробуем удалить созданный файл. Действие должно быть заблокировано.

Создадим себе новую флешку, вставим её в Windows и попробуем создать папку. Что может быть проще?



Скриншот на момент окончания сценария


Создаём новый текстовый файл через контекстное меню проводника:



Скриншот после переименования файла


Отключаем флешку, делаем это безопасно:



Теперь мы убедились, что с флешкой работать можно, а значит можно приступать к её блокировке в центре безопасности Dr. Web. Для этого сначала надо открыть центр безопасности:



Скриншот с окном Security Center


Можем заметить, что для открытия любого приложения в Windows нужно выполнить фактически одинаковые действия (кликнуть на строку поиска, дождаться появления окна с популярными приложениями, вбить имя интересующего приложения, дождаться, когда оно появится в списке и, наконец, нажать Enter). Поэтому эту группу действий можно выделить в макрос open_app, в который в качестве параметра будет передаваться имя открываемого приложения:



Этот макрос нам ещё пригодится.


Первое, что мы сделаем, открыв центр безопасности Dr. Web включим возможность вносить изменения:



Теперь немного покликаем по менюшкам и зайдем в меню "Configure device access rules". В этом меню поставим галочку в пункте "Block removable media".



Скриншот с окном Devices and Personal Data


Попробуем открыть флешку теперь:



Скриншот с сообщение об ошибке


Вот так потихоньку-полегоньку мы и написали первый тест с тестированием вполне осязаемой фичи в Dr. Web. Настало время передохнуть и помедитировать, глядя на результаты наших трудов:




Тест 3 Разграничение доступа к каталогу между программами


Основная идея этого тесткейса проверить работу Dr. Web при ограничении доступа к определенной папке. Если конкретно, то необходимо защитить папку от каких-либо изменений, но добавить исключение для какой-нибудь сторонней программы. Собственно, сам тест выглядит следующим образом:


  1. Установим на ОС стороннюю программу, для которой чуть позже добавим исключение при доступе к защищаемой папке. Сегодня сторонняя программа дня файловый менеджер FreeCommander;
  2. Создаем папку с файликом, которую будем защищать всеми силами;
  3. Откроем центр безопасности Dr. Web и включим там защиту этой папки;
  4. Настроим исключение для FreeCommander;
  5. Попробуем удалить файл из защищаемой папки обычным способом (через проводник Windows). Не должно получиться;
  6. Попробуем удалить файлик через FreeCommander. Должно получиться.

Ух, много работы. Быстрее начнём быстрее закончим.


Пункт первый, установка FreeCommander не сильно отличается от установки Dr.Web. Обычная рутина: вставили флешку, запустили инсталлятор и так далее. Пропустим это и перейдём сразу к интересному.


Если всё-таки интересно, как установить FreeCommander

Начнём с простого: создадим флешку, в которую поместим дистрибутив FreeCommander, а затем в тесте вставим флешку в ОС и откроем её:



Далее несколько не кликов чтобы запустить установку:



Установка не очень интересная, просто кликаем везде "Далее", а в конце не забываем отключить галочки с просмотром ReadMe и немедленным запуском FreeCommander



Заканчиваем тест, закрывая все окна и вытаскивая флешку



Готово!


Для работы с Dr. Web создадим новый тест dr_web_restrict_program, который будет полагаться на результат работы предыдущего теста win10_install_freecommander.


Начнём тест с создания папки Protected на рабочем столе:



Скриншот после создания папки


Заходим в папку Protected и создаём там файл my_file.txt, который будет играть роль защищаемого файла:



Ох, надо было бы тоже оформить это в виде макроса, ну да ладно ...


Скриншот после создания файла


Отлично, теперь надо включить защиту папки. Идём знакомой дорожкой и открываем Dr. Web, не забываем включить режим изменений. После чего переходим в меню "Data Loss Prevention".



Скриншот с окном Data Loss Prevention


Немного поработаем мышкой и добавим нашу папку Protected в список защищаемых:



Скриншот с мастером добавления защищаемой папки


Ну а теперь надо настроить исключение по доступу к папке для FreeCommander. Ещё немного работы мышкой:



Скриншот с добавленной программой-исключением


Теперь аккуратно закрываем все окна и пробуем удалить файл "my_file.txt" стандартным способом:



Скриншот с сообщением от Dr.Web


Но ничего не получилось значит Dr. Web действительно отработал! Половина теста позади, но нам ещё надо проверить, что будет работать исключение для FreeCommander. Для этого открываем FreeCommander и переходим в папку Protected:



Скриншот с окном FreeCommander


Ну и попробуем удалить файл my_file.txt:



Скриншот после удаления файла


Исключение для FreeCommander работает!


Отличная работа! Большой и сложный тесткейс и всё автоматизировано. Немного расслабона:




Тест 4 Родительский контроль


Этот последний на сегодня тесткейс мы построим следующим образом:


  1. Создадим нового пользователя MySuperUser;
  2. Залогинимся под этим пользователем;
  3. Создадим файл my_file.txt от имени нового пользователя;
  4. Откроем центр безопасности Dr. Web и включим родительский контроль для этого файла;
  5. В родительском контроле ограничим права пользователя MySuperUser на им же созданный файл;
  6. Попробуем прочитать и удалить файл my_file.txt от имени MySuperUser и посмотрим на результат.

Я не буду приводить здесь сценарий теста. Он строится по тому же принципу, что и предыдущие тесты: активно работаем мышкой и клавиатурой. При этом нам не важно, что мы автоматизируем хоть Dr.Web, хоть создание нового пользователя в Windows. Но давайте всё же посмотрим, как будем выглядеть прогон такого теста:



Заключение


Исходники всех тестов Вы можете посмотреть здесь


Более того, все эти тесты Вы можете прогнать на своей машине. Для этого Вам потребуется интерпретатор тестовых сценариев Testo. Скачать его можно здесь.


Dr. Web оказался неплохой тренировкой, но вдохновение для дальнейших подвигов я хотел бы почерпнуть из Ваших пожеланий. Пишите в комментариях свои предложения о том, какие автотесты Вы бы хотели увидеть в будущем. В следующей статье я попробую их автоматизировать, посмотрим, что из этого получится.

Подробнее..

После года разработки вышел эмулятор QEMU 6.0

30.04.2021 20:15:00 | Автор: admin

Прошел ровно год с момента выхода эмулятора QEMU 5.0, и сейчас разработчики представили новую версию. Проект дает возможность запустить программное обеспечение, подготовленное для одной архитектуры, на аппаратной платформе с другой архитектурой.

Пример выполнение приложения для ARM на ПК с процессором x86. Благодаря особенностям эмулятора приложение в изолированном окружении выполняется почти с той же эффективностью, что и в нативном окружении. Достигается это, в частности, за счет прямого выполнения инструкций на процессоре, а также за счет применения гипервизора Xen или модуля KVM. Сейчас эмулятор поддерживает 14 аппаратных архитектур и может эмулировать около 400 разных устройств. За год разработчики внесли свыше 3 тысяч изменений от 268 разработчиков.

Подробнее о ключевых изменениях


Понятно, что объем статьи не позволит внести все 3 тысячи изменений, поэтому ограничимся ключевыми, наиболее важными:

  • Эмулятор контроллеров NVMe теперь приведен к полному соответствию со спецификацией NVMe 1.4. Также он получил экспериментальную поддержку нескольких новых фич, включая зонированные пространства имен, multipath I/O и сквозное шифрование данных на накопителе.
  • Кроме того, добавлены экспериментальные опции "-machine x-remote" и "-device x-pci-proxy-dev", которые нужны для выноса эмуляции устройств во внешние процессы. В текущем режиме эмулятор поддерживает пока лишь SCSI-адаптер lsi53c895.
  • Есть экспериментальная поддержка снапшотов содержимого ОЗУ.
  • Появился новый FUSE-модуль, который необходим для экспорта блочных устройств. Он дает возможность примонтировать срез состояния практически любого блочного устройства, которое используется в гостевой системе. Эксперт при этом реализуется благодаря QMP-команде block-export-add или через опцию "--export" в утилите qemu-storage-daemon.
  • Важное обновление получил эмулятор архитектуры ARM так, добавлены архитектуры ARMv8.1-M 'Helium' и процессоров Cortex-M55, а также расширенных инструкций ARMv8.4 TTST, SEL2 и DIT. А еще появилась поддержка ARM-плат mps3-an524 и mps3-an547, плюс добавлена дополнительная эмуляция устройств для плат xlnx-zynqmp, xlnx-versal, sbsa-ref, npcm7xx.
  • Что касается режимов эмуляции на уровне системы для ARM, то здесь появилась поддержка расширения ARMv8.5 MTE (MemTag, Memory Tagging Extension). Оно позволяет привязать теги к каждой операции выделения памяти, организовав при доступе к ней проверку указателя. Последний должен быть связан с корректным тегом. Для чего потребовалось расширение? Оно дает возможность блокировать эксплуатацию уязвимостей, которые вызваны обращением к уже освобожденным блокам памяти, переполнениями буфера, обращениями до инициализации и использованием вне текущего контекста.
  • В эмуляторе для 68k разработчики добавили новый тип эмулируемых машин virt. Нововведение позволяет использовать для оптимизации производительности устройства virtio.
  • В архитектуре x86 появилась возможность применения технологии AMD SEV-ES (Secure Encrypted Virtualization) для шифрования регистров процессора, используемых в гостевой системе. В итоге содержимое регистров недоступно для хост-окружения в том случае, если гостевая система не предоставляет к ним явный доступ.
  • Для архитектуры MIPS также добавлен новый тип эмулируемых машин virt с поддержкой новых китайских процессоров Loongson-3.
  • Для PowerPC, точнее, для эмулируемых машин добавлена поддержка внешних BMC-контроллеров. Если случается сбой с pseries из-за попытки горячего извлечения памяти и CPU, то обеспечивается информирование о сбоях.
  • Появилась поддержка эмуляции процессоров Qualcomm Hexagon c DSP.
  • Реализована поддержка хост-окружений macOS на системах с новым ARM-чипом Apple M1 в классическом генераторе кода TCG (Tiny Code Generator).
  • При эмуляции архитектуры RISC-V для плат Microchip PolarFire реализована поддержка QSPI NOR flash.
  • В эмуляторе Tricore добавлена поддержка новой модели плат TriBoard, эмулирующей SoC Infineon TC27x.
  • В ACPI-эмуляторе теперь можно назначать сетевые адаптеры в гостевых системах имен, которые не зависят от порядка подключения к шине PCI.

С полным списком изменений и дополнений можно ознакомиться вот здесь.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru