Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fix hanging and leaks in readLine

After the last line is read from the fileDescriptor the callee will call
readLine once last time. If the (uninitialized) buffer just happened to
contain '\r' then this method would increment bytesReceived, then
decrement it (because it has '\r' in it), then decrement it agin and
assign a null byte to the byte before the buffer (stepping on who knows
what). Then it would return the '\r'. Then, since it received something,
the callee would call readLine again, malloc would give the same buffer
it did before (with the '\r') and everything would repeat.

    - initialize the buffer
    - increment bytesReceived only if a byte is actually received
    - don't do any work in the loop if there were no bytes received
    - EINTR is a recoverable error, just reread
    - give the actual reason for an error rather than some random string
    - free the buffer when a newline is found or when there is an error
  • Loading branch information...
commit dcba769fd34e9e728420546260ef729a60aeea4c 1 parent 98920bf
@brotherbard authored
Showing with 25 additions and 21 deletions.
  1. +25 −21 NSFileHandleExt.m
View
46 NSFileHandleExt.m
@@ -27,42 +27,46 @@ -(NSString*)readLine {
char *buffer = (char*)malloc(bufferSize + 1);
if (buffer == NULL)
[[NSException exceptionWithName:@"No memory left" reason:@"No more memory for allocating buffer" userInfo:nil] raise];
+ buffer[0] = '\0';
- int bytesReceived = 0, n = 1;
+ int bytesReceived = 0, n = 0;
- while (n > 0) {
- n = read(fd, buffer + bytesReceived++, 1);
+ while (1) {
+ n = read(fd, buffer + bytesReceived, 1);
+
+ if (n == 0)
+ break;
if (n < 0) {
- if (errno == EINTR) {
- n = 1;
- bytesReceived--;
- } else {
- [[NSException exceptionWithName:@"Socket error" reason:@"Remote host closed connection" userInfo:nil] raise];
- }
- }
+ if (errno == EINTR)
+ continue;
+
+ free(buffer);
+ NSString *reason = [NSString stringWithFormat:@"%s:%d: read() error: %s", __PRETTY_FUNCTION__, __LINE__, strerror(errno)];
+ [[NSException exceptionWithName:@"Socket error" reason:reason userInfo:nil] raise];
+ }
+
+ bytesReceived++;
if (bytesReceived >= bufferSize) {
// Make buffer bigger
bufferSize += BUFFER_SIZE;
- buffer = (char*)realloc(buffer, bufferSize + 1);
+ buffer = (char *)reallocf(buffer, bufferSize + 1);
if (buffer == NULL)
[[NSException exceptionWithName:@"No memory left" reason:@"No more memory for allocating buffer" userInfo:nil] raise];
}
- switch (*(buffer + bytesReceived - 1)) {
- case '\n':
- buffer[bytesReceived-1] = '\0';
- NSString* s = [NSString stringWithCString: buffer encoding: NSUTF8StringEncoding];
- if ([s length] == 0)
- s = [NSString stringWithCString: buffer encoding: NSISOLatin1StringEncoding];
- return s;
- case '\r':
- bytesReceived--;
+ char receivedByte = buffer[bytesReceived-1];
+ if (receivedByte == '\n') {
+ bytesReceived--;
+ break;
}
+
+ if (receivedByte == '\r')
+ bytesReceived--;
}
- buffer[bytesReceived-1] = '\0';
+ buffer[bytesReceived] = '\0';
NSString *retVal = [NSString stringWithCString: buffer encoding: NSUTF8StringEncoding];
if ([retVal length] == 0)
retVal = [NSString stringWithCString: buffer encoding: NSISOLatin1StringEncoding];
Please sign in to comment.
Something went wrong with that request. Please try again.