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)
Ответ на: комментарий от Virtuos86

Я настолько отвык от этих сишных нелепостей, что вообще не ожидал такой подставы. Пару дней ковырялся в инглишдоках, наконец нашел примечание. Вольный перевод:


Стандарты C и POSIX указывают, что поведение sprintf и его вариантов не определено, если аргумент перекрывается целевым буфером. Пример:

sprintf ( dst, «% s и% s» , dst, t ) ; // <- broken: undefined behavior

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

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

anonymous
()
Ответ на: комментарий от Xintrea

Пару дней ковырялся в инглишдоках, наконец нашел примечание.
Стандарты C и POSIX указывают, что поведение sprintf и его вариантов не определено, если аргумент перекрывается целевым буфером

Ох, бро.

Ещё раз, это не тайное знание. Это написано в прототипе функции.

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

Я специально дал ссылку на описание ключевого слова restrict, чтобы в следующий раз, ты смотрел прототип используемой функции, чтуь более внимательно. Именно оно и указывает, что buffer и format не должны перекрываться(в том числе и с необязательными параметрами (...))

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

Ещё раз, это не тайное знание. Это написано в прототипе функции.

Ссылку ты дал хорошую, и тебе спасибо что она на русском.

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

Мне вот например показалось, что restrict buffer - это одна сущность «обособленный буфер». Я даже не знал, что в сях есть ключевое слово restrict, и даже искать его не стал.

У меня была такая же проблема с консольным zip. Я несколько лет не мог понять как им пользоваться, использовал по образцам команд из интернета, но не мог понять, почему они такие. А дело оказалось в том, что в форматной строке в официальной документации написано:

zip [-options] [-b path] [-t mmddyyyy] [-n suffixes] [zipfile list] [-xi list]

Так вот этот [zipfile list] я переводил и как «списочный zip-файл» и как «список zip-файлов» (думал там «s» не хватает), но в любом случае я эту часть воспринимал как часть команды, которая работает с какими-то списками в zip-файле, что мне было ненужно. Мне нужно было уметь упаковать один файл, или файлы в одной директории. И именно этого я не мог понять как делать, пока до меня не дошло, что [zipfile list] следует рассматривать как [имя_zip-файла список_файлов_помещаемых_в_архив].

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

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

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

Это от неправильного понимания языка. И со временем вы от него огребете.

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

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

Нет, конструкции — это строка на вход транслятора

asyncron2018
()
Ответ на: комментарий от anonymous

Аргументированно

Так же как у тебя. А аргументов было выше крыши хотя бы в тредах про UB.

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

в тредах про UB

Можно ссылочку? Или что тебе конкретно не нравится в виде кейворда для гугла.

anonymous
()
Ответ на: комментарий от asyncron2018

Это никогда никого не останавливает.

anonymous
()
Ответ на: комментарий от asyncron2018

Если вы оба про undefined behavior, то я про него знаю. Не подразумевал, что транслируется однозначно.

anonymous
()
Ответ на: комментарий от Xintrea

Тебе лучше вообще не программировать, если ты искал в доках то что написанно в САМОМ начале обычного мана по этим функциям.

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

А иногда я вижу, что в документации допускают некоторые вольности.

Какие б**дь вольности, что ты несешь? Ты тут вроде давно. Да просто читая много раз англо доку(может даже по несколько раз) со словарем можно за год можно привыкнуть к стилю и выучить необходимый словарный запас для чтения манов.

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

zip [-options] [-b path] [-t mmddyyyy] [-n suffixes] [zipfile list] [-xi list]

ты где такую доку нашел? У меня на системе и в гугле только

zip [-aABcdDeEfFghjklLmoqrRSTuvVwXyz!@$] [--longoption ...] [-b path] [-n suffixes] [-t date] [-tt date] [zipfile [file ...]] [-xi list]

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

С(++) нужно воспринимать как(часто тонкий) слой сахара над ассемблером.

Много вас таких по весне в UB-тредах оттаивает.

anonymous
()
Ответ на: комментарий от Dudraug

В cygwin так

Copyright (C) 1990-1993 Mark Adler, Richard B. Wales, Jean-loup Gailly
and Kai Uwe Rommel. Type 'zip -L' for the software License.

Zip 2.0.1 (Sept 18th 1993). Usage:
zip [-options] [-b path] [-t mmddyy] [-n suffixes] [zipfile list] [-xi list]
  The default action is to add or replace zipfile entries from list, which
  can include the special name - to compress standard input.
  If zipfile and list are omitted, zip compresses stdin to stdout.
  -f   freshen: only changed files  -u   update: only changed or new files
  -d   delete entries in zipfile    -m   move into zipfile (delete files)
  -k   simulate PKZIP made zipfile  -g   allow growing existing zipfile
  -r   recurse into directories     -j   junk (don't record) directory names
....
....
[\code]

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

Можно ссылочку?

Из последних: Вызов никогда не вызываемой функции О неопределённом поведении и багах оптимизатора clang на примере разбора цикла статей Криса Латтнера, разработчика clang. Читать - про абстрактную семантику, есть даже в стандарте C.

Правда, я не понял как всё это относится к вопросу ТС.

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

Аааа, это хэлп (который выводится при вызове без параметров) В man zip все адекватно же. Каки в гугле.

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