avva: (Default)
[personal profile] avva
(интересно только программистам, знающим C)

Майкрософтовский strlen(), оказывается, не проверяет, не передали ли ему случаем NULL, а по-простому читает то, что по переданному адресу находится, и падает кверху лапками.

Если майкрософтовскому printf'у передать NULL в качестве аргумента, соответствующего %s в строке формата, он это проверит и выдаст "(null)".

Однако у Glib'а (библиотека низкого уровня, лежащая в основе GTK, в основном использующаяся на юниксах, но по идее поддерживающая и платформу Win32) есть внутри своя имплементация printf'а (а точнее, vasnprintf()'а, чтобы покрыть все возможные случаи). Она не делает полностью всю работу printf'а, а всего лишь обрабатывает строку формата и аргументы и строит отдельные более простые вызовы snprintf/sprintf (хотя бы sprintf уже должен быть везде) для каждого аргумента, размещая их по очереди в буфере возврата. Glib использует эту имплементацию в том случае, когда у платформы, на которой её строят, нет своего достаточно хорошего и поддерживающего всё, что нужно, printf'а. Win32 - одна из таких платформ.

Если этому внутреннему Glib'овскому printf'у передать NULL в качестве аргумента, соответствующего %s в форматной строке, то он в определённый момент вызовет strlen() на этот аргумент, чтобы узнать длину строки аргумента - для того, чтобы создать буфер достаточной длины. Увы, авторам этого printf'а не пришло в голову, что strlen на какой-то платформе может упасть, если передать ему NULL. Поэтому они это не проверяют, и в результате, если вызвать что-то вроде: g_printf("foo: %s\n", NULL), то программа упадёт.

Это всё я обнаружил, пытаясь построить и заставить что-то делать ЖЖ-клиент LogJam под Windows XP.

Интересно, кто виноват? Аппликация, передающая какой-либо версии printf'а аргумент NULL для подстановки к %s? Имплементация printf'а, вызывающая strlen(), не проверяя аргумент на ==NULL? Имплементация strlen(), идущая сразу по адресу аргумента, не проверяя на NULL?

P.S. Проверил strlen() на юниксах (glibc). Он тоже, оказывается, не проверяет на NULL. Но там тот же баг не проявлялся потому, что GLib использует системный printf, а не свой, а системный это дело проверяет.

Date: 2005-03-27 10:36 am (UTC)
stas: (Default)
From: [personal profile] stas
Насколько я знаю, никто не обязывает strlen проверять NULL - NULL не есть легитимная строка. То, что printf печатает (null) - это неожиданная удача (точнее, подарок от авторов libc криворуким программистам ;), но полагаться на это, ПМСМ, ни в коем случае не следует.
В данном случае виновата, конечно, программа, передающая printf-у NULL. Но виноват также и glib, не полностью поддерживающий интерфейс printf, то есть не проверяющий строки на NULL.

Date: 2005-03-27 02:18 pm (UTC)
From: [identity profile] arbat.livejournal.com
я бы добавил следующее: и не надо обязывать strlen проверять на NULL. Предположим, проверили, ай-яй-яй, это - NULL. Ну и? Что теперь делать? Exceptions в C не предусмотрены. Возвращаем мы size_t, т.е. совместить возврат осмысленного числа с вовзратом кода ошибки - не удастся. Ну, или - ценой потери половины диапазона заменой типа возврата на int, или ценой возврата, скажем, максимально значения в качестве кода ошибки, надеясь, что программист, который забыл проверить параметр на NULL - не забудет проверить возвращаемый код, да еще не ошибиться - как. Но, главное - это делать абсолютно незачем. Если клиент функции хочет сделать проверку, он может. Если он не хочет, одна из главных идеологий C состоит в том, что клиент не должен платить за то, что он не заказывал. Потому, вполне правильный выбор - не проверять. При вызове с NULL обещать - undefined behavior.

printf... ну, это, предположительно, функция медленная. Ей по барабану потратить чуток времени на NULL проверить, да и ответ на вопрос "что же делать, если это NULL?" там более понятен. Что про нее стандарт говорит - не знаю.

Date: 2005-03-27 04:45 pm (UTC)
stas: (Default)
From: [personal profile] stas
В printf дело скорее не в том, что она медленная, а в том, что большого вреда от неё не будет, если в "непределённых" случаях она будет писать, что захочет. Хотя если вспомнить историю с %n...

Date: 2005-03-27 05:18 pm (UTC)
From: [identity profile] arbat.livejournal.com
ну, да. СОбственно, для printf - NULL не есть имманентно "неправильный" вход. Для strlen - это однозначно за пределами допустимого диапазона. А printf, да - может вполне последовательно и разумно NULL обработать.
(deleted comment)

Date: 2005-03-27 06:47 pm (UTC)
From: [identity profile] avva.livejournal.com
Отлично ;)

starbucks-shmarbucks

Date: 2005-03-27 07:06 pm (UTC)
From: [identity profile] arbat.livejournal.com
"If you understand computers, you know that a computer normally is immune to the character of the data it processes,” he wrote in the June U.S. Naval Institute’s Proceedings Magazine. “Your $2.95 calculator, for example, gives you a zero when you try to divide a number by zero, and does not stop executing the next set of instructions. It seems that the computers on the Yorktown were not designed to tolerate such a simple failure." (http://www.gcn.com/17_17/news/33727-1.html)

December 2025

S M T W T F S
  123 4 56
78 9 10 11 1213
1415 1617181920
21 22 23 24 2526 27
28293031   

Most Popular Tags

Page Summary

Style Credit

Expand Cut Tags

No cut tags
Page generated Dec. 29th, 2025 01:46 am
Powered by Dreamwidth Studios