[devel] вопрос по языку Си - порядок вычисления операндов

Kharitonov A. Dmitry kharpost at rambler.ru
Thu Oct 1 08:31:39 UTC 2009


Alexey Tourbin wrote:
> У нас в rpm есть такой код.
>
> lib/depends.h:
>    108  typedef /*@abstract@*/ struct availableList_s {
>    109  /*@owned@*/ /*@null@*/ struct availablePackage * list;  /*!< Set of packages. */
>    110      struct availableIndex index;        /*!< Set of available items. */
>    111      int delta;                          /*!< Delta for pkg list reallocation. */
>    112      int size;                           /*!< No. of pkgs in list. */
>    113      int alloced;                        /*!< No. of pkgs allocated for list. */
>    114      int numDirs;                        /*!< No. of directories. */
>    115  /*@owned@*/ /*@null@*/ dirInfo dirs;    /*!< Set of directories. */
>    116  } * availableList;
> ...
>
> lib/depends.c:
>    213  static /*@exposed@*/ struct availablePackage *
>    214  alAddPackage(availableList al,
>    215                  Header h, /*@null@*/ /*@dependent@*/ const void * key,
>    216                  /*@null@*/ FD_t fd, /*@null@*/ rpmRelocation * relocs)
>    217          /*@modifies al, h @*/
>    218  {
>    219      HGE_t hge = (HGE_t)headerGetEntryMinMemory;
>    220      HFD_t hfd = headerFreeData;
>    221      rpmTagType dnt, bnt;
>    222      struct availablePackage * p;
>    223      rpmRelocation * r;
>    224      int i;
>    225      int_32 * dirIndexes;
>    226      const char ** dirNames;
>    227      int numDirs, dirNum;
>    228      int * dirMapping;
>    229      struct dirInfo_s dirNeedle;
>    230      dirInfo dirMatch;
>    231      int first, last, fileNum;
>    232      int origNumDirs;
>    233      int pkgNum;
>    234  
>    235      if (al->size == al->alloced) {
>    236          al->alloced += al->delta;
>    237          al->list = xrealloc(al->list, sizeof(*al->list) * al->alloced);
>    238      }
> ...
>    703  int rpmtransAddPackage(rpmTransactionSet ts, Header h, FD_t fd,
>    704                          const void * key, int upgrade, rpmRelocation * relocs)
>    705  {
>    706      HGE_t hge = (HGE_t)headerGetEntryMinMemory;
>    707      HFD_t hfd = headerFreeData;
>    708      rpmTagType ont, ovt;
>    709      /* this is an install followed by uninstalls */
>    710      const char * name;
>    711      int count;
>    712      const char ** obsoletes;
>    713      int alNum;
>    714  
>    715      /*
>    716       * FIXME: handling upgrades like this is *almost* okay. It doesn't
>    717       * check to make sure we're upgrading to a newer version, and it
>    718       * makes it difficult to generate a return code based on the number of
>    719       * packages which failed.
>    720       */
>    721      if (ts->orderCount == ts->orderAlloced) {
>    722          ts->orderAlloced += ts->delta;
>    723          ts->order = xrealloc(ts->order, sizeof(*ts->order) * ts->orderAlloced);
>    724      }
>    725      ts->order[ts->orderCount].type = TR_ADDED;
>    726      if (ts->addedPackages.list == NULL)
>    727          return 0;
>    728  
>    729      alNum = alAddPackage(&ts->addedPackages, h, key, fd, relocs) -
>    730                  ts->addedPackages.list;
>    731      ts->order[ts->orderCount++].u.addedIndex = alNum;
> ...
>
> Вопрос касается строк 729-730.  Следите за движением рук.
>
> ts->addedPackages -- это некоторая структура данных, которая содержит
> ts->addedPackages.list -- указатель на массив, выделяемый в куче.
> Функция alAddPackage() добавляет новый элемент в этот массив и возвращает
> на него указатель.  Соответственно (по правилам типизированной адресной
> арифметики) переменная alNum -- это будет индекс последнего добавленного
> элемента в массиве.
>
> Но!  Когда массив становится слишком маленьким, функция alAddPackage()
> делает realloc этого массива (строка 237).  Соотвественно, адрес массива
> ts->addedPackages.list может измениться.
>
> Теперь посмотрим на вычитание указателей в строках 729-730.  Левый
> операнд вычитания может изменить ts->addedPackages.list, а правым
> операндом является сам ts->addedPackages.list.  Получается, что этот
> код зависит от порядка вычисления операндов -- а именно, может
> использоваться либо старое значение ts->addedPackages.list, либо
> уже новое значение.
>
> Воопрос соответствено насколько легален этот код, и вообще любая
> нетривиальная информация на эту тему.
>   
Неоднократно натыкался на такое:
исходное выражение а+b+c
менялся порядок вычисления случайным образом
то так
а+(b+c)
то так
(а+b)+c
то так
(с+b)+a
естественно получалась случайно работающая программа. Багу ловил 3 месяца.
Во всех руководствах написано, что расстановка скобок принудительно 
должна обеспечивать порядок вычислений, но я опять таки натыкался на 
отступления и от этого. Разбивание выражения с применением промежуточной 
переменной однозначно определяло порядок, именно тот, который мне нужен. 
Промежуточная переменная в конечном коде в результате оптимизации исчезала.
Так же трудно уловимый баг связан с подстановкой в аргументы функции 
скрытых временных особенно, если эти аргументы помеченны модификатором 
const.



More information about the Devel mailing list