Программа использует лексический сканер для классификации токенов как символа, строки, десятичного числа, шестнадцатеричного числа и т. Д. При обнаружении «числа» оно передается в strtol()
для преобразования его во внутреннее 32-разрядное двоичное значение. Однако я не могу заставить strtol()
надежно вернуть ошибку при переполнении.
Часть кода конверсии:
errno = 0; // erase any previous error in errno
switch (constType) {
…
case lxHex: // hexadecimal number X'1234567890ABCDEF' (X-string)
fprintf(stderr,"** FindConstantFromString - converting %s\n",constBuffer);
newDictEntry->dcValue = strtol(constBuffer+2, NULL, 16);
int myerr = errno;
fprintf(stderr," value %x errno %d\n",newDictEntry->dcValue, myerr);
newDictEntry->dcType = syNumber;
newDictEntry->dcSubType = 4; // hexadecimal
if ( EINVAL == errno
|| ERANGE == errno
) {
ErrDict = newDictEntry;
AnaError (ConstMsg+2);
newDictEntry->dcType = sySLit;
};
result.cstClass = newDictEntry->dcType;
return result;
…
Когда этот код проверяется с неправильным вводом, он обнаруживает переполнение, только если первая шестнадцатеричная цифра> = 8 (потенциально дает отрицательное значение), как продемонстрировано:
29 | declare v;
30 | v = x'fedcba9876543210'
** FindConstantFromString - meeting x'fedcba9876543210' as 11
** FindConstantFromString - converting x'fedcba9876543210'
value ffffffff errno 34
*Error 32: Candidate number x'fedcba9876543210' too large or could not be converted
*Error 20: Unrecognisable lexical unit x'fedcba9876543210' at 30.5
31 | + x'123456789abcdef'
** FindConstantFromString - meeting x'123456789abcdef' as 11
** FindConstantFromString - converting x'123456789abcdef'
value 89abcdef errno 0
32 | + 9876543210
** FindConstantFromString - meeting x'fedcba9876543210' as 8
symbol already known
** FindConstantFromString - converting x'fedcba9876543210'
value 0 errno 0
*Error 32: Candidate number x'fedcba9876543210' too large or could not be converted
** FindConstantFromString - meeting 9876543210 as 8
** FindConstantFromString - converting 9876543210
value 4cb016ea errno 0
33 | + '12345a'
** FindConstantFromString - meeting 12345a as 3
34 | + '';
** FindConstantFromString - meeting 12345a as 8
symbol already known
** FindConstantFromString - converting 12345a
value 3039 errno 0
*Error 32: Candidate number 12345a too large or could not be converted
** FindConstantFromString - meeting as 3
** FindConstantFromString - meeting as 8
symbol already known
*Error 33: Empty string cannot be converted to number
В строке 30 лексический сканер распознал шестнадцатеричное число и запросил преобразование из этой шестнадцатеричной формы (11 = lxHex). strtol()
правильно устанавливает errno
на ERANGE
и выдается сообщение об ошибке. Переполненное шестнадцатеричное число затем сохраняется в словаре в виде строки.
Обратите внимание, что возвращаемое значение равно -1, а не LONG_MAX.
В строке 31 у нас снова есть другое переполняющееся шестнадцатеричное число, но оно не начинается с 8-9a-f. Он снова определяется как шестнадцатеричное число. Попытка преобразования предпринята, но значение errno не установлено вообще. Значение соответствует младшим 32 битам числа. Поскольку это считается успешным, усеченное значение сохраняется как результат.
Когда +
применяется к «x'fed…» и 89abcdef, предпринимается попытка другого преобразования для строки «x'fed…», которая должна быть десятичным числом (обозначается как 8-запрос) и преобразование завершается неудачно, потому что «x» не может начинать десятичное число.
В строке 32 имеется десятичное число переполнения 987654321. Еще раз, переполнение не обнаружено (код не показан, но похож на тот, что используется для шестнадцатеричных чисел с добавлением теста для "endptr", поскольку строки не могут быть отфильтрованы лексическим сканером и содержат недопустимые символы). Возвращаемое значение содержит как минимум 32 бита числа.
Если я изменю strtol()
на strtoul()
, первая ошибка ERANGE
исчезнет, и я получу как минимум 32 бита числа.
Что я делаю не так?
Система: Fedora Linux 29
glibc: 2,27