7

I'm introducing myself to socket programming in C/C++, and am using send() and recv() to exchange data between a client and server program over TCP sockets.

Here are some relevant excerpts from my code:

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);

Does recv() itself provide any protection against buffer overflows? For instance if I changed the 3rd argument to recv() to something higher than the buffer pointed to by recv_data (e.g. 4000) - would this cause a buffer overflow? (I've actually tried doing this, but can't seem to trigger a segfault).

I'm actually trying to create an intentionally vulnerable server program to better understand these issues, which is why I've tried to overflow via recv().

Amendment:

Not unrelated, would be finding out why client.c above would ever send more than the 1024 bytes specified by strlen(send_data). I'm using gets(send_data) to populate that buffer from standard input, but if I enter many more than 1024 bytes via standard in, the server.c program shows that it receives ALL THE BYTES! :). Does the strlen(send_data) for send() not restrict the number of bytes sent?

DJSunny
  • 1,970
  • 3
  • 19
  • 27
  • 1
    Nothing to do with your question, but you spelt received wrong. Better to correct it now than later. :-) – James M Mar 03 '13 at 15:51
  • 1
    Buffer overruns are always a mistake in the program, but they only _sometimes_ lead to segfaults, depending on how libc is laying out memory behind the scenes. There could also be corrupted data, or everything can appear to work normally, as you observed. That's why buffer overruns can be so hard to track down. – gcbenison Mar 03 '13 at 16:04

3 Answers3

11

For instance if I changed the 3rd argument to recv() to something higher than the buffer pointed to by recv_data (e.g. 4000) - would this cause a buffer overflow?

Ofcourse yes. If the network buffer has data of 4000 bytes, it will put it in the buffer. The key point is that, recv like any other C API which takes a buffer and it's length believes that the caller will pass the actual length of the buffer and if the caller passes incorrect length, then the fault lies with the caller and it can lead to undefined behavior.

In C when you pass arrays to a function, there is no way for the called function to know the size of the array. So, all API(s) just rely on the input provided by you.

char recv_data[1024];

// Socket setup and so on ommited...

bytes_recieved = recv(connected, recv_data, 1024, 0);
recv_data[bytes_recieved] = '\0';

The above code can cause trouble in more ways than one. It will lead to undefined behavior under the following conditions:
(a) If recv returns -1, then you are directly indexing into the recv_data buffer without checking the return value
(b) If recv returns 1024, then again, it leads to out of bound access as the array of size 1024 should be accessed from 0 to 1023.

Linuxios
  • 34,849
  • 13
  • 91
  • 116
Jay
  • 24,173
  • 25
  • 93
  • 141
  • It seems like if the server.c `recv_data` buffer is say 4000 bytes, and I specify the length argument in the `recv()` call to be say 3000 bytes, and I sent 5000 from the client - the first call to `recv()` will only copy 3000 bytes, but if I call it again it will receive 1000 bytes. So the (network buffer?) must be holding onto that data that has yet to be handled - which is why I would see it come in subsequent calls to `recv()`? – DJSunny Mar 03 '13 at 16:21
  • Yes. Ofcourse. The network buffer will hold on to the data and give you only as much data as is the size of the buffer. – Jay Mar 04 '13 at 04:38
  • @csjohn: it's important to understand that TCP is a byte-stream protocol... that means that the packets can be split or reassembled during transmission. If you send 5000 from the client, even if the receiver asks for 3000 it may get 1..3000 bytes without that being an error. It's up to the receiver to reason about whether that's enough data to process else reassemble it with data from other `recv()` calls. E.g. in your scenario above, even *if* the first `recv()` gets 3000 bytes, the second call could get 50 and the third 950 (or more if another `send()` had happened). – Tony Delroy Mar 04 '13 at 07:34
6

This

recv_data[bytes_recieved] = '\0';

could result in a buffer overflow, if 1024 bytes were received.

You might like to change this

bytes_recieved = recv(connected, recv_data, 1024, 0);

to become

bytes_recieved = recv(connected, recv_data, 1024 - 1, 0);

so that bytes_recieved would never become larger than 1023, which is the maximum valid index to recv_data.


Also your system calls (recv()/send()) lack error checking. Test them for having returned -1 prior to using the result in any other way.


Referring your amendment:

strlen() tries to return the number of characters starting from the character pointed to by its argument up until the first NUL/0-character. This number could be any value, depending on where you placed the terminating 0.

In case the seach for this 0-terminator runs behind the memory allocated to strlen()s argument the program most certainly runs into undefined behaviour, and therefore could return any value.

So to answer your question: If send_data is not 0-terminated strlen() makes the app run into undefined behaviuor so it might crash or strlen() returns a value greater than 1024, so send() would try to send this number of characters.

alk
  • 69,737
  • 10
  • 105
  • 255
  • I suspected as much, though I can't seem to trigger a segfault. I've changed the call to be something like `recv(connected, recv_data, 4048, 0);`. On the client side, the `send_data` buffer is only 1024 bytes, but when I spam say 5000 bytes of input, the server still shows that `bytes_received` is 5000 bytes. – DJSunny Mar 03 '13 at 15:52
  • 1
    @csjohn what makes you think that a buffer overflow only exists if it triggers a segfault? – jalf Mar 03 '13 at 15:53
  • @jalf Nothing, I just *feel* like the magnitude of the garbage I was throwing in would have triggered one :). – DJSunny Mar 03 '13 at 15:56
  • 1
    @csjohn you have no guarantee of that. When you're *lucky* a buffer overflow results in a segfault. That's a nice, clean outcome which can be debugged, and which doesn't lead to further corruption of program state or data. But most of the time, you're not lucky. So you have to be on guard yourself, and avoid any such overflows occurring even if they *seemingly* cause no harm. Because the keyword is "seemingly". – jalf Mar 03 '13 at 22:09
  • @jalf Yeah - Thanks for the input. I've never *really* had to do anything serious in C/C++ (except an operating system course in school), so I've been spoiled by the affordable of higher level languages. – DJSunny Mar 03 '13 at 22:16
0

Even if you send larger bytes than the recv() buffer, you are still able to recv() it on succeeding calls to recv(), that's why you said that bytes_received is still 5000 bytes, because, let's say you send 5000 bytes, and your receive buffer is 1000 bytes, on the first call to recv() it will only get 1000 bytes, on the next call, 1000 bytes again, until it receives all your data. So, I think there's no buffer overflow here. This is by the way how TCP works.

TravellingGeek
  • 1,581
  • 11
  • 16