strlen() ломает исходный строковый литерал

211
16 апреля 2017, 02:22

В общем столкнулся с таким непонятным багом. Есть функция, которая упаковывает исходное сообщение в некий пакет. Для этого она находит длину исходного сообщения. и вот тут то вызывается strlen(message);. message - const char* из аргумента функции. до вызова strlen это обычная строка, после вызова однако она коверкается до неузнаваемости. Собирал g++ и clang++ оба с -O0 -g. однако повторить в минимальном варианте вне кода самой программы не получается.

Что тут вообще может случиться, что меняется строковый литерал?

это вызывается из конструктора одного класса:
{
const char* reuest = scom::create_auth_request("User");
socket->send(request);
}

const char* scom::create_auth_request(const char* name)
{
  const char* result;
  int len;
  len = strlen(name);
  char buff[1 + len];
  sprintf(buff, "%01d%s", AUTH_REQUEST, name); /* AUTH_REQUEST = 0 */
  result = buff;
  return result;
}

void scom::ClientSocket::send(int connection, const char* message)
{
  unsigned int total = 0;
  /* здесь message = "0User" */
  unsigned int len = strlen(message);
  /* здесь message уже "h\364\n\br" */

  /* остальное уже в принципе не важно */
  /*
  if(len > 1024)
    throw scom::Exception();
  unsigned int bytesleft = len + 2;
  unsigned int n;
  char to_send[len + 2];
  memset(to_send, 0, len + 2);
  uint16_t nlen = htons(len);
  to_send[0] = nlen>>8;
  to_send[1] = nlen;
  strcpy(to_send+2, message);
  while(total < len + 2)
  {
    n = ::send(connection, to_send+total, bytesleft, 0);
    if(n == -1)
      break;
    total += n;
    bytesleft -= n;
  }
  if(n == -1)
    throw scom::Send();
}
*/
Answer 1

посмотрел на scom::create_auth_request - там возвращается указатель на локальный буфер. По выходу с функции он конечно в памяти остается и даже данные по нему живые, но когда вызывается следующая функция, она перетирает стек своими переменными - она же не знает о том, что там остались данные. Это абсолютно совпадает с наблюдаемым поведением. В Вашем случае этот буфер перетирает собственно strlen.

Как можно ещё проверить, что это именно так. Перепишите функцию отправки так

void scom::ClientSocket::send(int connection, const char* message)
{
  int i = 0;
  int j = 0;
  int z = 0;
  unsigned int total = 0;
  /* здесь message = "0User" */

нужно именно как минимум 8 байт, потому что в create_auth_request буфер находиться как минимум с 9 байта. Но компилятор может это дело все переставить.

Как это пофиксить. Первый способ - передать буфер buff как параметр функции и хранить его вызывающей программе. Второй способ - выделить память явно через malloc. Третий способ - писать все таки на с++ и использовать std::string вместо char*.

B.T.W. - Вы похоже используете VLA - а это gcc специфик.

READ ALSO
Перенести код С++ с std::map на СИ

Перенести код С++ с std::map на СИ

Есть код на С++ в нём используются std::vector и std::mapМне нужно перенести код на язык СИ где нет STL, делать реализацию методов из STL vector и map для массива...

192
Ассемблер/C++ как вызвать функцию пользовательского класса класса?

Ассемблер/C++ как вызвать функцию пользовательского класса класса?

Допустим у меня есть модуль/класс: TGAImage, а внутри него функция:

200
как будет выглядеть в СИ?

как будет выглядеть в СИ?

это в файле maincpp Кусок метода который нужно сделать в си коде

184
Бинарный поиск для строк

Бинарный поиск для строк

Не может найти нужный элемент в отсортированном массиве

242