3.2
3.3 разработчик программного обеспечения: Организация, которая выполняет разработку программного обеспечения (в том числе анализ требований, проектирование, приемочные испытания) в ходе всего жизненного цикла программного обеспечения.
3.4
3.5
3.6 безопасный компилятор: Компилятор, не вносящий в ходе компиляции в бинарный код программы ошибки, которых не было в исходном коде программы.
Примечания
1 Безопасный компилятор не удаляет ошибки, имеющиеся в исходном коде программы, а пытается избежать таких преобразований кода, в результате которых после окончания компиляции в результирующем бинарном коде программы появляются ошибки, которых не было в исходном коде. Безопасный компилятор может добавлять в ходе компиляции бинарный код, который обеспечивает защиту от выполнения неопределенных конструкций в ходе выполнения программы.
2 Общеупотребительна практика называть компилятором C/C++ командную оболочку, выполняющую последовательность согласованных трансляций: препроцессирование, собственно компиляцию, ассемблирование, статическую компоновку. В связи с этим в настоящем стандарте при необходимости, кроме требований к безопасному компилятору, приводятся требования к соответствующим инструментальным средствам системы программирования языков C/C++.
3.7 неопределенная конструкция: Элемент программы на языке высокого уровня (выражение, оператор и другие), поведение которого в ходе выполнения программы не определено стандартами соответствующих языков.
Примечание - Использование неопределенных конструкций в исходном коде программы является ошибочным, и при выполнении оптимизационных преобразований компиляторы вправе полагаться на отсутствие в исходном коде программы конструкций с неопределенным поведением. В случае наличия таких конструкций в бинарный код программы компилятором может быть внесена ошибка. На практике в программах на языках C/C++ неопределенные конструкции встречаются часто.
3.8 побочный эффект: Изменение состояния среды выполнения программы (изменение ячейки памяти, файла, вызов функции, выполняющей эти действия, и другие).
Примечание - Принято считать, что при выполнении программы сохранение ею результатов ее работы является побочным эффектом.
3.9 датчик срабатывания ошибок: Конструкция исходного или бинарного кода, которая вставляется в программу при ее сборке из исходного кода, интерпретации или двоичной трансляции и которая при обнаружении определенной ошибки в ходе выполнения программы вызывает ее аварийный останов и/или выдачу диагностической информации.
3.10 санитайзер: Компонент компилятора, встраивающий датчики срабатывания ошибок в генерируемый код во время компиляции программы.
3.11
3.12
3.13 автоматическая память: Память, хранящая локальные переменные и аргументы функции программы только во время выполнения этой функции.
3.14 статическая память: Память, хранящая глобальные переменные, константы, функции программы в течение всего времени выполнения программы.
3.15 динамическая компоновка: Процесс формирования окончательной бинарной программы для исполнения, выполняемый динамическим загрузчиком во время загрузки исполняемого файла или работы программы.
3.16 статическая компоновка: Процесс формирования бинарной программы (загрузочного модуля) из одного или нескольких объектных модулей путем разрешения ссылок между модулями и, при необходимости, настройки адресов.
3.17 исполняемый файл: Бинарный файл, содержащий исполняемый модуль программы, который может быть запущен в среде выполнения программы.
3.18 стандартная библиотека языка программирования: Набор констант, переменных, процедур, классов и т.п., которые присутствуют во всех реализациях языка программирования.
Примечание - Состав стандартной библиотеки и поведение ее частей для языков C/C++ зафиксированы в стандартах соответствующих языков.
4.1 Использование безопасного компилятора позволяет не вносить в бинарный код программы ошибки, которых не было в исходном коде программы, тем самым уменьшается вероятность реализации угроз внедрения уязвимостей программы из-за неверного использования инструментальных средств при разработке программного обеспечения (см. ГОСТ Р 58412). Безопасный компилятор не выполняет оптимизационные преобразования программы, если обнаруживает неопределенные конструкции, и не полагается на отсутствие таких конструкций в программе. Безопасный компилятор также оповещает пользователя об обнаруженных неопределенных конструкциях, внедряет в исполняемую программу код для динамического контроля таких конструкций (в случае их обнаружения в ходе работы программы происходит ее аварийное завершение), обеспечивает журналирование процесса трансляции для построения соответствия между исходным кодом программы и сгенерированным компилятором бинарным кодом.
4.2 Безопасный компилятор компилирует исходный код программы на языке высокого уровня в семантически эквивалентный бинарный код. В ходе компиляции безопасный компилятор выполняет преобразования исходного и бинарного кода для улучшения характеристик получаемого бинарного кода - в том числе скорости, размера и обеспечения безопасного функционирования кода.
4.3 Во время компиляции при выполнении преобразований исходного и бинарного кода безопасный компилятор должен избегать использования свойств неопределенных конструкций, а также оповещать пользователя о наличии таких конструкций в исходном коде программы и обеспечивать защиту от выполнения неопределенных конструкций во время работы программы.
4.4 В состав безопасного компилятора должны быть включены:
а) компоненты безопасных преобразований исходного и машинного кода, не использующие свойства неопределенных конструкций;
б) компоненты управления конфигурацией процесса трансляции;
в) компоненты оповещения пользователя о наличии в исходном коде неопределенных конструкций;
г) компоненты преобразования исходного кода с целью добавления в программу машинного кода, обеспечивающего защиту от потенциально ошибочного выполнения неопределенных конструкций во время работы программы (далее - динамический контроль неопределенных конструкций);
д) компоненты управления распределением автоматической и статической памяти (далее - управление распределением памяти).
4.5 Требования настоящего стандарта формулируются в отношении компилятора. Тем не менее, система программирования языков C/C++ предполагает выполнение согласованной последовательности этапов трансляции: препроцессирования, собственно компиляции, ассемблирования и статической компоновки. В связи с этим требования настоящего стандарта при технологической необходимости (в части компонентов управления распределением автоматической и статической памяти) распространяются на всю совокупность программных инструментов, обеспечивающих трансляцию исходных текстов C/C++ в исполняемый код, включая используемые библиотеки поддержки времени выполнения.
4.6 Для соответствия требованиям настоящего стандарта разработчик программного обеспечения должен использовать для компиляции программ на языках C/C++ безопасный компилятор для этих языков.
Соответствие требованиям настоящего стандарта достигается:
а) разработкой нового компилятора;
б) доработкой уже существующего компилятора;
в) должной настройкой конфигурации уже существующего компилятора при компиляции программ.
4.7 Для дифференциации требований к безопасному компилятору выделяются три класса безопасного компилятора. Самый низкий класс - третий, самый высокий - первый. Классы безопасного компилятора определяют баланс между безопасностью генерируемого кода (меньшим количеством остающихся в коде уязвимостей либо большей защитой от имеющихся уязвимостей) и производительностью генерируемого кода (более высокие классы требуют реализации большего количества функций безопасного компилятора и ведут к большему замедлению получающейся программы). Допустимые замедления программы для каждого класса компилятора приведены в 5.1.
Примечание - В приложении А дается перечень опций компилятора GCC, использование которых позволяет отвечать требованиям третьего класса [за исключением 5.2.1 г), д); 5.2.5, 5.2.6 б)].
4.8 Для соответствия требованиям настоящего стандарта в части реализации функций безопасности безопасный компилятор программ на языках C/C++ должен соответствовать требованиям раздела 5.
4.9 Для соответствия требованиям настоящего стандарта в части допустимых замедлений получаемых программ безопасный компилятор программ на языках C/C++ должен соответствовать требованиям раздела 6.
4.10 Проверка соответствия безопасного компилятора требованиям разделов 5 и 6 проводится согласно требованиям, приведенным в разделе 7.
5.1 Безопасный компилятор соответствующего класса должен реализовывать функции безопасности, приведенные в таблице 1 для каждого класса.
Таблица 1
5.2 Безопасный компилятор 3 класса должен реализовывать следующие функции.
5.2.1 Для реализации функции по безопасным преобразованиям исходного и машинного кода безопасный компилятор при выполнении преобразований должен предполагать следующее:
а) при выполнении операций над целыми числами может возникнуть знаковое целочисленное переполнение во всех случаях, когда с помощью анализа значений переменных и условий выполнения операций не доказано обратное;
б) значения указателей различающихся типов могут совпадать при операциях чтения и записи данных через эти указатели (далее - разыменование указателя);
в) при разыменовании указателя данный указатель может содержать нулевое значение во всех случаях, когда с помощью анализа условий выполнения разыменования указателя не может быть доказано обратное;
г) при выполнении целочисленных операций деления и взятия остатка значение делителя может быть равно нулю, если с помощью анализа возможных значений переменных и условий выполнения операций над целыми числами не доказано обратное;
д) при выполнении операций побитового сдвига значение второго аргумента (величина сдвига) может быть отрицательным либо оказаться больше или равным ширине типа (количеству занимаемых битов) первого аргумента операции (сдвигаемого значения), если с помощью анализа возможных значений переменных и условий выполнения операций над целыми числами не доказано обратное.
5.2.2 Для реализации функции по безопасным преобразованиям исходного и машинного кода безопасный компилятор должен обеспечивать следующие свойства генерируемого кода:
а) при вызове функций стандартной библиотеки языка C (C++) должен быть реализован механизм защиты от переполнения буфера постоянного (заданного константой во время компиляции) размера;
б) в процессе трансляции в машинный код должен быть добавлен механизм обнаружения записи, выходящей за границу кадра стека, кроме случаев, когда применение этого механизма невозможно (например, в коде ядра или загрузчика операционной системы, в коде реализации самого этого механизма);
в) генерируемый код должен быть позиционно-независимым, кроме случаев, когда это не применимо (например, в коде ядра операционных систем). При этом код, который в дальнейшем будет загружаться динамическим загрузчиком, должен быть позиционно-независимым как для случая сборки программы для получения динамических библиотек, так и для получения исполняемого файла. Если из-за ограничений операционной системы данное требование выполнить невозможно, генерируемый код должен допускать перебазирование (загрузку по произвольным адресам);
г) вызовы функций стандартной библиотеки fprintf, fwprintf, printf и wprintf не могут быть заменены на эквивалентные им последовательности машинных инструкций (для функций из этого списка требование распространяется также на компиляторы более высоких 2 и 1 классов защиты);
д) вызовы функций стандартной библиотеки memcpy, memmove, memset, strcat, strcpy, strncat, strncpy, snprintf, sprintf, swprintf, wcscat, wcscpy, wcsncat, wcsncpy, wmemcpy, wmemmove и wmemset не могут быть заменены на эквивалентные им последовательности машинных инструкций (данное требование действует только на компиляторы 3 класса и не переносится на более высокие классы, так как во втором классе присутствуют требования, касающиеся любых операций записи в память).
Примечание - После встраивания тела функции (последовательности машинных инструкций) в место ее вызова в ходе дальнейших оптимизаций некоторые инструкции могут быть удалены. Безопасный компилятор должен сохранить все инструкции.
5.2.3 Для реализации функции по оповещению пользователя о наличии в исходном коде неопределенных конструкций безопасный компилятор должен в процессе трансляции выдавать предупреждения при наступлении следующих событий:
а) обнаружена переменная, размещенная в автоматической памяти, которая хранится на машинном регистре перед вызовом функции longjmp, и ее значение используется после вызова этой функции путем загрузки из соответствующего регистра;
б) обнаружена операция чтения или записи в массив по адресу, который находится за пределами выделенной памяти для этого массива;
в) обнаружена целочисленная операция деления или взятия остатка, при которой значение делителя равно нулю;
г) обнаружена операция побитового сдвига, в которой второй аргумент операции сдвига (величина сдвига) отрицателен либо больше или равен ширине типа (количеству занимаемых битов) первого аргумента операции (сдвигаемого значения).
5.2.4 Для реализации функции по управлению конфигурацией процесса трансляции безопасный компилятор при наличии небезопасного режима работы должен содержать механизм включения и отключения функциональности, реализующей функции третьего класса.
5.2.5 Для реализации функции по журналированию процесса трансляции безопасный компилятор должен фиксировать в базе данных компиляции для каждой единицы трансляции:
а) рабочую директорию компиляции;
б) имя файла, представляющего единицу трансляции;
в) хэш-код (контрольную сумму) файла, представляющего единицу трансляции;
г) выполняемую команду компиляции целиком или полный список аргументов компиляции;
д) имя выходного файла, получаемого в результате компиляции;
е) хэш-код (контрольную сумму) выходного файла;
ж) в случае включения в единицу трансляции файлов, например при препроцессировании, - имя каждого включаемого файла и его хэш-код.
Для ведения базы данных компиляции должен использоваться текстовый формат обмена данными JSON (RFC 8259), совместимый с форматом базы данных компиляции компилятора clang. Пример записи:
[
{ "directory": "/opt/ejudge",
"command": "/usr/bin/gcc -g -Wall -std=gnu99 -Wno-pointer-sign -c -o file.o
file.c",
"file": "file.c" },
...
]
База данных компиляции состоит из массива записей, описывающих трансляцию одной единицы трансляции.
В базу данных компиляции следует вносить информацию, позволяющую идентифицировать программы, задействованные в трансляции исходных текстов в исполняемый код, а также все используемые настройки этих программ и конфигурационные файлы.
Должна поддерживаться возможность по завершении компиляции программы зафиксировать состояние базы данных компиляции, вычислив контрольную сумму содержимого базы данных.
Для подсчета контрольных сумм как единиц трансляции, так и самой базы данных компиляции должна использоваться функция хэширования, определенная ГОСТ 34.11.
5.2.6 Для реализации функции по управлению механизмами защищенности безопасный компилятор должен дополнительно включать в ходе компиляции реализованные в нем либо совместно с операционной системой механизмы повышения защищенности кода [помимо обязательных для реализации свойств кодогенерации из 5.2.2 б), в)]. Например, такими механизмами являются:
5.3 Безопасный компилятор 2 класса должен реализовывать следующие функции.
5.3.1 Для реализации функции по безопасным преобразованиям исходного и машинного кода безопасный компилятор должен удовлетворять всем требованиям, установленным 5.2.1, и всем требованиям, установленным 5.2.2, за исключением перечисления д).
Также безопасным компилятором должны обеспечиваться следующие свойства генерируемого кода:
а) при наличии хотя бы одного обращения к участку памяти (чтения) должны быть сохранены (оставлены) все побочные эффекты для всех операций записи этого участка (как предшествующих, так и последующих);
б) все переменные из автоматической памяти, для которых не задано значение инициализации, должны иметь нулевое значение инициализации.
Дополнительно при выполнении преобразований безопасный компилятор должен предполагать следующее:
а) при выполнении операции побитового сдвига в случае, когда значение второго аргумента (величина сдвига) отрицательно либо больше или равно ширине типа (количеству занимаемых битов) первого аргумента операции (сдвигаемого значения), результат вычисления может зависеть от особенностей процессорной архитектуры и не может быть заменен на фиксированное значение;
б) при выполнении операций над векторными машинными регистрами и памятью используемые адреса объектов векторных типов данных в памяти могут иметь произвольные значения (не кратные размеру типов);
в) при выполнении операций сложения и вычитания со значениями адресов (далее - адресная арифметика) результаты операций могут иметь произвольные значения.
5.3.2 Для реализации функции по оповещению пользователя о наличии в исходном коде неопределенных конструкций безопасный компилятор должен в процессе трансляции выдавать ошибку и аварийно останавливать выполнение трансляции при наступлении следующих событий:
а) любом из событий, перечисленных в 5.2.3;
б) в исходном коде программы используется функция gets.
5.3.3 Для реализации функции по управлению конфигурацией процесса трансляции безопасный компилятор при наличии менее безопасных режимов работы должен содержать механизм включения и отключения функциональности, реализующей функции второго или одновременно третьего и второго классов.
5.4 Безопасный компилятор 1 класса должен реализовывать следующие функции.
5.4.1 Для реализации функции по безопасным преобразованиям исходного и машинного кода безопасный компилятор должен удовлетворять 5.3.1.
5.4.2 Для реализации функции по оповещению пользователя о наличии в исходном коде неопределенных конструкций безопасный компилятор должен удовлетворять 5.3.2.
5.4.3 Для реализации функции по динамическому контролю неопределенных конструкций безопасный компилятор должен обеспечить добавление в процессе трансляции программы машинного кода, обеспечивающего защиту от ошибочного выполнения неопределенных конструкций во время работы программы. При выполнении неопределенных конструкций работа программы должна быть аварийно остановлена. В случае, когда аварийная остановка является неприемлемой (для некоторых видов системного программного обеспечения, например, ядра операционной системы, гипервизоров 1-го типа и др.), допускается применение иных механизмов защиты. Должны поддерживаться следующие неопределенные конструкции:
а) операция записи значения в переменную булевого типа, которое отлично от лжи и истины;
б) операция преобразования значения переменной вещественного типа с плавающей запятой в переменную другого типа (целочисленного или с плавающей запятой), приводящая к целочисленному или вещественному переполнению;
в) операция целочисленного сдвига, в которой второй аргумент операции сдвига (величина сдвига) отрицателен либо не меньше ширины типа (количества занимаемых битов) первого аргумента операции (сдвигаемого значения);
г) операция со знаковыми целочисленными переменными, результат которой не может быть представлен в результирующей переменной;
д) операция чтения или записи по указателю, значение которого не кратно размеру типа хранящегося по этому адресу объекта;
е) операция чтения или записи по указателю, значение которого равно нулю;
ж) операция чтения или записи в массив по адресу, находящемуся за пределами выделенной для массива памяти, если размер памяти может быть определен в процессе трансляции;
и) операция адресной арифметики, приводящая к целочисленному переполнению;
к) операция вызова по указателю функции, формальный тип которой не соответствует фактическому типу указателя;
л) окончание работы функции, имеющей возвращаемое значение, без выполнения операции возврата;
н) операция вызова встроенных функций компилятора, не подлежащих вызову;
п) операция целочисленного деления или взятия остатка, значения делителя в которой равно нулю;
р) выделение в автоматической памяти массива переменной длины, размер которого не является положительным.
5.4.4 Для реализации функции по управлению распределением памяти безопасный компилятор должен обеспечить возможность при каждом запуске программы на выполнение выполнять уникальное (особое для данного запуска) распределение в памяти машинного кода функций в процессе динамической компоновки программы.
Для реализации данного требования, наряду с его поддержкой в компиляторе, также необходима соответствующая поддержка со стороны динамического загрузчика операционной системы.
При наличии в операционной системе динамического загрузчика данное требование не распространяется на ядро операционной системы, а также системные программы, для запуска которых не используется динамический загрузчик (в том числе на сам динамический загрузчик).
При сборке программ для архитектур, которые не предусматривают наличие динамического загрузчика, а также для операционных систем, в которых невозможна реализация собственного динамического загрузчика, данное требование заменяется требованием статического уникального распределения памяти.
Во всех случаях, когда организация динамического распределения в памяти невозможна, безопасный компилятор должен обеспечить возможность уникального распределения автоматической и статической памяти программы полностью на этапе ее компиляции. Компилятор должен поддерживать следующие возможности при управлении распределением:
а) распределение памяти должно конфигурироваться целочисленным параметром (ключом), при этом для одинаковых задаваемых ключей и одного и того же исходного кода компилятор должен создавать одинаковое распределение памяти;
б) возможность уникального распределения локальных переменных функций для каждого процесса трансляции;
в) возможность уникального распределения памяти для машинного кода функций для каждого процесса трансляции;
г) возможность уникального распределения глобальных переменных программного модуля в соответствующей области памяти для каждого процесса трансляции.
5.4.5 Для реализации функции по управлению конфигурацией процесса трансляции безопасный компилятор при наличии менее безопасных режимов работы должен содержать механизм включения и отключения функциональности, реализующей функции первого, первого и второго или всех трех классов.
Замедление производительности генерируемого безопасным компилятором кода (времени работы кода) по сравнению с производительностью кода, генерируемого в небезопасном режиме работы этого же компилятора (в котором не реализуются функции 5.1), должно составлять:
а) для 3 класса - не более 20%;
б) для 2 класса - не более 50%;
в) для 1 класса - не более 200%.
7.1 Соответствие безопасного компилятора требованиям, изложенным в разделах 5 и 6, проверяется с помощью набора квалификационных тестов. Запуск тестов должен осуществляться с помощью автоматизированной системы тестирования.
7.2 Соответствие безопасного компилятора требованиям, изложенным в разделе 6 (проверка производительности сгенерированного компилятором кода), проверяется с помощью сравнения производительности сгенерированного машинного кода с производительностью кода, созданного эталонным компилятором в небезопасном режиме работы, при условии включения стандартного режима оптимизаций компилятора (например, для компилятора GCC такой режим соответствует опции - O2).
Для выполнения проверки система тестирования производительности компилирует наборы тестов на производительность два раза: первый раз используется эталонный компилятор, в котором отключены все функции безопасности раздела 5, во второй раз - безопасный компилятор со всеми включенными функциями безопасности. После компиляции оба варианта тестовых программ запускаются на выполнение на одинаковых входных данных на платформе, на которой предполагается функционирование собираемой компилятором программы, и в зависимости от времени выполнения система генерирует интегральную оценку производительности. Разница в оценке производительности должна соответствовать требованиям раздела 6.
7.3 Соответствие безопасного компилятора требованиям раздела 5 проверяется с помощью тестов следующих типов:
а) ассемблерные тесты, которые проверяют, что в скомпилированной программе будут находиться или отсутствовать определенные конструкции;
б) тесты времени выполнения, которые проверяют какой-либо результат работы скомпилированной программы (обычно возвращаемое значение или данные стандартного потока вывода);
в) тесты, которые проверяют работу санитайзера: во время работы программы датчики срабатывания ошибок, встроенные санитайзером во время компиляции, должны обнаружить операции с неопределенным поведением и аварийно завершить программу;
г) тесты на наличие предупреждений, которые проверяют, что в процессе компиляции будут выданы требуемые предупреждения.
7.4 Безопасный компилятор можно считать удовлетворяющим выбранному классу, если он проходит все тесты из группы, соответствующей этому классу, а также все тесты из групп, соответствующих менее безопасным классам.
(справочное)
КОМПИЛЯТОРА 3 КЛАССА И ОПЦИЙ КОМПИЛЯТОРА GCC
Таблица А.1
(справочное)
ПРИМЕР ПОСТРОЕНИЯ КВАЛИФИКАЦИОННЫХ ТЕСТОВ
ДЛЯ ПРОВЕРКИ ТРЕБОВАНИЙ РАЗДЕЛА 5
1) пункт 5.2.1 в) - в приведенном тесте безопасный компилятор не должен из разыменования p->z делать вывод, что p - ненулевой указатель, и удалять вызов функции bar(). Этот вызов должен присутствовать в ассемблерном коде, сгенерированном безопасным компилятором.
struct point {
int x,y,z;
} *p;
extern void ret_point(const char *v);
extern void g(int x);
extern void bar(int x);
int c;
int result = 2;
int f(const char *v) {
// устанавливает значение p
ret_point(v);
c = p->z;
if (!p) {
result--;
bar(result);
return -1;
}
g(c);
return 0;
}
int main(void) {
const char s[] = "0123";
result = result + f(s);
return result;
}
2) пункт 5.2.1 б) - в приведенном тесте безопасный компилятор из разницы в базовых типах указателей x и y не должен делать вывод, что они указывают на разные участки памяти, и всегда возвращать 0 из функции foo. Тем самым после компиляции указанного теста запуск скомпилированного кода должен выполняться успешно (функция main должна возвращать 0 на архитектуре x86).
int foo(int *x, long *y) {
*x = 0;
*y = 1;
return *x;
}
int main(void) {
long l;
return 1 - foo((int *)&l, &l);
}
3) пункт 5.3.1 а) - в приведенном тесте безопасный компилятор должен сохранить побочные эффекты обоих вызовов memset. Оба вызова должны присутствовать в ассемблерном коде, сгенерированном безопасным компилятором.
#include <string.h>
void f(char *);
int main(void) {
char a[1000];
f(a);
memset(a, '*', 1000);
memset(a, '/', 1000);
return 0;
}
4) пункт 5.3.1 б) - в приведенном тесте безопасный компилятор должен инициализировать нулями массив d на каждой итерации цикла. Это проверяется во внешней функции check_arr, а функции fill и use заполняют и используют массив d.
int check_arr(int *d, unsigned n);
void fill(int *d, unsigned n);
void use(int *d, unsigned n);
int main(int argc, char **argv)
{
unsigned n;
if (argc == 1)
argc = 32;
else
return 2;
n = argc;
while (--argc)
{
int d[n];
if (check_arr(d, argc))
return 1;
fill(d, argc);
use(d, argc);
}
return 0;
}
5) пункт 5.4.3 ж) - в приведенном тесте безопасный компилятор должен обеспечить обнаружение ошибки выхода за границы выделенной памяти во время выполнения программы для случая, когда размер памяти может быть определен во время компиляции.
int foo(int x) {
return x - 3;
}
int main(int argc, char **argv) {
int a[4] = {0, 1, 2, 3};
return (argc - 1) * a[foo(argc + 1)];
}
6) пункт 5.4.3 м) - в приведенном тесте безопасный компилятор должен обеспечить обнаружение ошибки целочисленного деления на ноль во время выполнения программы. Тест запускается на наборах входных данных, включающих деление на 0 и деление INT_MIN на -1.
#include <limits.h>
volatile int t;
int main(int argc, char **argv) {
int x = argc, y = argc - 1;
// Компилятор не имеет права удалять запись в volatile переменную. Это делает
// результат деления "полезным" и не позволяет удалить операцию деления.
t = x / y;
x = INT_MIN;
y = -argc;
t = x / y;
return 0;
}
Вернуться в "Каталог нормативных документов"
Источник информации: https://internet-law.ru/documents/prod/gost-r_gosudarstvennyj-standart/18/gost_79978.html
На правах рекламы:
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||