LINUX.ORG.RU

Оказывается, snprintf() таки небезопасна

 ,


2

5

Не люблю я Си-шные строки, не люблю эти низкоуровневые пляски со строками. Но вот понадобилось сделать интерфейс с такими строками. На первый взгляд все выглядело кошерно. Но полезли баги, и я чего только не передумал, пока не додебажился до проблемы в snprintf().

Так вот, оказывается snprintf() не умеет писать строку саму в себя. Зачем это нужно? Ну вот например код:

// connectUrl - имя/IP сервера (поле класса)
// connectPort - порт сервера (поле класса)

bool TcpChannel::open(const char *url, int port)
{
    // Запоминаются URL и порт
    snprintf(connectUrl, TCP_CHANNEL_MAX_URL_LEN, url);
    connectPort=port;

    // Устанавливается соединение (возвращает void)
    tcpSocket->connectToHost(connectUrl, connectPort);
    ...
}

void TcpChannel::reConnect()
{
    ... всякая логика ...

    // Переустанавливается соединение
    open(connectUrl, connectPort);
}

Тут поле connectUrl из reConnect() передается в open() и должно записаться само в себя, то есть, вообще ничего не должно измениться по-хорошему. А вот болт. Первый байт строки будет обнулен и получится пустая строка. Не ожидал я такого. Как люди пишут на сях с такими строками - не понимаю.

★★★★★

Последнее исправление: cetjs2 (всего исправлений: 1)

Так вот, оказывается snprintf() не умеет писать строку саму в себя. Не ожидал я такого. Как люди пишут на сях с такими строками - не понимаю

Фейспалм бро...

Смотри как описывается snprintf

int snprintf( char *restrict buffer, size_t bufsz,
              const char *restrict format, ... );

Теперь читаем описание restrict https://ru.wikipedia.org/wiki/Restrict

fsb4000 ★★★★★
()
Последнее исправление: fsb4000 (всего исправлений: 1)
Ответ на: комментарий от fsb4000

Вот это тоже жесть, особенно в контексте разговора про безопасность использования:

snprintf(connectUrl, TCP_CHANNEL_MAX_URL_LEN, url);
i-rinat ★★★★★
()

snprintf(connectUrl, TCP_CHANNEL_MAX_URL_LEN, url);

А в чём вообще смысл этого вызова? Это же получается почти strncpy() с неожиданными спецэффектами, если в url какое-нибудь говно попадёт.

Короче, я не понял, что именно ты пытаешься тут сделать.

Ну и плюсом ты не проверяешь возвращаемое значение.

Deleted
()
Последнее исправление: Deleted (всего исправлений: 1)

Ждём треда о том, что memcpy не копирует между пересекающимися областями.

Или копирует, но какая-то херня получается.

Как люди пишут на сях с таким memcpy - не понимаю.

aol ★★★★★
()
Последнее исправление: aol (всего исправлений: 1)
Ответ на: комментарий от aol

Ну ты бы намекнул как-нибудь, что у тебя тут многоходовка намечается.

i-rinat ★★★★★
()

Расходимся, это Xintrea снова возомнил себя сишником.

Deleted
()

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

ckotinko ☆☆☆
()

snprintf(connectUrl, TCP_CHANNEL_MAX_URL_LEN, url);

Это что такое? А потом в урле окажется % и будет много интересного

vromanov ★★
()
Ответ на: комментарий от vromanov

А ты любитель тащить валидации внутрь методов выполняющих тупые задачи?

Хотя, Xintrea... можно усомниться что у него есть валидация в другом месте. Тем не менее — в этом месте её быть не должно, в этот момент чисто архитектурно уже поздно пить боржоми.

deep-purple ★★★★★
()
Ответ на: комментарий от deep-purple

Это не проблема валидации. Это надежность приложения. Нельзя в *printf передавать в качестве формата строку, которую может ввести пользователь.

vromanov ★★
()
Ответ на: комментарий от vromanov

Здец. Надежность приложения в первую очередь зависит от валидации данных вводимых пользователем. Если ты провалидировал и тебе что-то не понравилось, то до *принтфа оно не доедет. Понял?

deep-purple ★★★★★
()
Ответ на: комментарий от deep-purple

в этот момент чисто архитектурно уже поздно пить боржоми.

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

Ты что ли «любитель быстрого»?

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Нет. Я любитель поставить один КПП на входе в предприятие, а не сажать по охраннику в каждый кабинет к сотрудникам, выполняюшим свои конкретные задачи.

Нет, я не про ассерты, проверку возврата и все такое, я про тупо валидацию вводимых данных.

deep-purple ★★★★★
()
Последнее исправление: deep-purple (всего исправлений: 1)
Ответ на: комментарий от deep-purple

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

vromanov ★★
()
Ответ на: комментарий от vromanov

Конечно, КПП же есть. Оно защищает на одном уровне. Но есть и другие уровни защиты, которые никак не относятся к валидации.

deep-purple ★★★★★
()
Ответ на: комментарий от deep-purple

Точно! Будку трансформатурную не закрывать. Да и провода высоковольтные можно не изолировать, если есть документация что их нельзя трогать. Месье, похоже, не слышал про Defensive programming

vromanov ★★
()

void QAbstractSocket::connectToHost(const QHostAddress &address, quint16 port, OpenMode openMode = ReadWrite)

Зачем в qt крестах вообще сишные танцы со строками?

ox55ff ★★★★★
()
Ответ на: комментарий от deep-purple

Я и говорю про другой уровень защиты не связанный с валидацией. Так просто нельзя делать. Нежелательно передавать в *printf строку формата, которая не известна во время компиляции, и уж точно нельзя туда передавать соку в котрой может оказаться все что угодно.

vromanov ★★
()
Последнее исправление: vromanov (всего исправлений: 1)
Ответ на: комментарий от deep-purple

Криворукий — если документацию не оставил.

Компилятор не читает документацию. Поэтому документация врёт.

i-rinat ★★★★★
()
Ответ на: комментарий от deep-purple

я про тупо валидацию вводимых данных.

Это верно, но это другое. Если код мгновенно вызывает реакцию: «чо за бред?!», нужно либо снабдить его комментарием, почему код написан именно так, а не иначе, либо написать нормально. Оправдания типа «я провалидировал» не катят. Сегодня ты помнишь, завтра — уже нет. Каждый раз будешь проверять заново?

Что вообще значит валидация для чего-то типа URL? В валидных URL вообще-то знаки процента могут быть. Или у тебя «провалидировал» URL означает «убрал знаки процента»?

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Как закодил, так и нескомпилилось. Компилятор же не криворукий, тем более, речь о рантайме.

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

deep-purple ★★★★★
()
Ответ на: комментарий от deep-purple

Я все понял. Надо будет добавить этот фрагмент кода в вопросы на собеседование чтобы криворуких отсеивать

vromanov ★★
()
Ответ на: комментарий от i-rinat

почему код написан именно так, а не иначе, сегодня ты помнишь, завтра — уже нет

Считай это «приватным» апи (как пример с натяжкой), но тебе туда все же можно, тыжпрограммист.

Или у тебя «провалидировал» URL означает «убрал знаки процента»

Нет, именно провалидировал. Ничего не убирая.

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

Хотя лично мне не нравится такая практика. На примере с браузерами — лучше бы они не пытались пофиксить невалидный хтмл/ДОМ, а тупо выдавали бы окошко типа: «ты лох, иди учись хтмл писать! стр 22, линия 18». Так бы и говнокодеров в вебе поменьше было.

deep-purple ★★★★★
()
Ответ на: комментарий от vromanov

Нет. Это ты не можешь хочешь понять о чем я. И вместо этого самоутверждаешься за счет попыток унизить/оскорбить. Ладно, я тоже хорош, вылез, квакнул в начале нашей ветки. Давай без этого, по делу.

deep-purple ★★★★★
()
Ответ на: комментарий от deep-purple

Блин, только сейчас осознал что ты вебельщик (php-шник?) и что я зря потратил на тебя три поста.

d_a ★★★★★
()
Ответ на: комментарий от deep-purple

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

А если он там не лишний (так как может быть часть URL), то твоя программа неожиданно упадёт в неожиданном месте. Или просто начнёт выводить ахинею, так как прочитает мусор после переменной.

monk ★★★★★
()
Ответ на: комментарий от d_a

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

deep-purple ★★★★★
()
Ответ на: комментарий от monk

А если он там не лишний

Ты конспиролог? Открываешь спеки и читаешь что там где может, а где не может быть. Или берешь готовый валидатор урл.

deep-purple ★★★★★
()
Ответ на: комментарий от monk

и d_a вы как-то не в ту сторону смотрите и все поймать меня на чем то не том норовите.

Нормально есть что возразить аргументами или будете дальше искать касяки в одёжке сказавшего что валидировать надо в одном месте и затем далее спокойно использовать данные?

Давайте. Я даже могу оказаться не прав. Никто не идеален.

deep-purple ★★★★★
()
Последнее исправление: deep-purple (всего исправлений: 1)
Ответ на: комментарий от vromanov

И где же меня ткнули носом в чем именно я не прав?

Мне приводили примеры не основной обсуждаемой проблемы, меня встречали по одежке. Но никто ничего не аргументировал.

Вот Ренат только нормально беседовал.

deep-purple ★★★★★
()
Ответ на: комментарий от deep-purple

Считай это «приватным» апи (как пример с натяжкой), но тебе туда все же можно, тыжпрограммист.

Хорошее API легко использовать правильно и сложно использовать неправильно. В такой формулировке хорошести продвигаемое тобой API — плохое. Если ты этого не понимаешь... Что ж. Не завидую тем, кто будет использовать писанные тобой библиотеки.

Нет, именно провалидировал. Ничего не убирая.

Мда.

На примере с браузерами — лучше бы они не пытались пофиксить невалидный хтмл/ДОМ, а тупо выдавали бы окошко типа: «ты лох, иди учись хтмл писать! стр 22, линия 18». Так бы и говнокодеров в вебе поменьше было.

XHTML уже существует. Не взлетел.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Хорошее API легко использовать правильно и сложно использовать неправильно

Да, особенно snprintf в частности и си в целом. Я имею ввиду что про вон си кричат «неосилятор», а тут как будто что-то другое.

Мда

А что не так? Показал окошко: «извините, вы ввели херню» и не работаешь дальше по этим данным.

XHTML уже существует

Даже его, родимого, втихую, пытается исправить.

deep-purple ★★★★★
()
Ответ на: комментарий от deep-purple

У меня подозрение, что ты вообще не понимаешь, как работает printf, и в какой момент он пытается подставить параметры в строку.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.