Я знакомлюсь с программированием сокетов на C / C ++ и использую send()
а также recv()
обмениваться данными между клиентской и серверной программой через TCP
Розетки.
Вот некоторые соответствующие выдержки из моего кода:
server.c:
char recv_data[1024];
// Socket setup and so on ommited...
bytes_recieved = recv(connected, recv_data, 1024, 0);
recv_data[bytes_recieved] = '\0';
client.c:
char send_data[1024];
// Setup ommited...
send(connected, send_data, strlen(send_data), 0);
Есть ли recv()
сам обеспечивает какую-либо защиту от переполнения буфера? Например, если я изменил третий аргумент на recv()
к чему-то выше, чем буфер, на который указывает recv_data
(например, 4000) — это приведет к переполнению буфера? (Я на самом деле пытался сделать это, но, похоже, не может вызвать segfault).
Я на самом деле пытаюсь создать намеренно уязвимую серверную программу, чтобы лучше понять эти проблемы, поэтому я попытался переполнить через recv()
,
поправка:
Не связано, было бы выяснить, почему client.c
выше, отправит больше, чем 1024
байты, указанные strlen(send_data)
, я использую gets(send_data)
заполнить этот буфер из стандартного ввода, но если я введу гораздо больше, чем 1024 байта через стандартный ввод, server.c
Программа показывает, что получает ВСЕ БАЙТЫ! :). Ли strlen(send_data)
за send()
не ограничивать количество отправляемых байтов?
Например, если я изменил 3-й аргумент на recv () на нечто большее, чем буфер, на который указывает recv_data (например, 4000) — это вызовет переполнение буфера?
Конечно да. Если сетевой буфер имеет данные 4000 байтов, он поместит их в буфер. Ключевым моментом является то, что recv, как и любой другой C API, который принимает буфер, и его длина считает, что вызывающая сторона передаст фактическую длину буфера, и если вызывающая сторона передает неправильную длину, то ошибка лежит на вызывающей стороне, и это может привести к неопределенному поведению.
В Си, когда вы передаете массивы в функцию, вызываемая функция не может узнать размер массива. Таким образом, все API просто полагаются на предоставленные вами данные.
char recv_data[1024];
// Socket setup and so on ommited...
bytes_recieved = recv(connected, recv_data, 1024, 0);
recv_data[bytes_recieved] = '\0';
Приведенный выше код может вызвать проблемы во многих отношениях, чем один. Это приведет к неопределенному поведению при следующих условиях:
(а) Если recv возвращается -1
, то вы напрямую индексируете в буфер recv_data без проверки возвращаемого значения
(б) Если recv возвращается 1024
опять же, это приводит к неконтролируемому доступу как массиву размера 1024
должны быть доступны из 0
в 1023
,
это
recv_data[bytes_recieved] = '\0';
может привести к переполнению буфера, если получено 1024 байта.
Вы можете изменить это
bytes_recieved = recv(connected, recv_data, 1024, 0);
становиться
bytes_recieved = recv(connected, recv_data, 1024 - 1, 0);
чтобы bytes_recieved
никогда не станет больше, чем 1023, который является максимальным допустимым индексом для recv_data
,
Также ваша система звонков (recv()
/send()
) отсутствует проверка ошибок. Проверьте их на предмет возвращения -1
до использования результата любым другим способом.
Ссылаясь на вашу поправку:
strlen()
пытается вернуть количество символов, начиная с символа, на который указывает его аргумент, до первого NUL
/0
-персонаж. Это число может быть любым значением, в зависимости от того, где вы поместил завершающий 0
,
В случае поиска для этого 0
-терминатор работает за памятью, выделенной для strlen()
С аргументом программа наверняка столкнется с неопределенным поведением и, следовательно, может вернуть любое значение.
Итак, чтобы ответить на ваш вопрос: если send_data
является не 0 концевыми strlen()
заставляет приложение работать с неопределенным поведением, чтобы оно могло аварийно завершиться или strlen()
возвращает значение больше 1024, поэтому send()
попытался бы отправить это количество символов.
Даже если вы отправите больше байтов, чем recv()
буфер, вы все еще можете recv()
это при последующих вызовах recv()
вот почему ты сказал, что bytes_received
все еще 5000
байты, потому что, скажем, вы отправляете 5000
байт, и ваш буфер приема 1000
байт, при первом вызове recv()
это только получит 1000
байтов при следующем вызове, 1000
байт снова, пока не получит все ваши данные. Итак, я думаю, что здесь нет переполнения буфера. Это, кстати, как работает TCP.