cURL / Mailing Lists / curl-users / Single Mail

curl-users

Crash in curl library while processing HTTP HEAD response

From: isshed <isshed.sip_at_gmail.com>
Date: Fri, 22 Jul 2016 18:54:14 +0530

Hi Experts,

I am new to the curl library. I am facing a crash in the curl library
while processing HTTP HEAD Request. Crash dump stack is showing like
as follows.

#############################
#0 0x40a91ab0 in ?? ()
#1 0x00463b48 in Curl_client_write (conn=0x15ea3d0, type=1, ptr=0x1
<Address 0x1 out of bounds>, len=0) at
/Code/ThirdParty/curl/lib/sendf.c:494
#2 0x00463b48 in Curl_client_write (conn=0x15ea3d0, type=1,
ptr=0x15f53d0 "404 - Error: Page Not Found.\r\n", len=30) at
/Code/ThirdParty/curl/lib/sendf.c:494
#3 0x00466704 in readwrite_data (done=<value optimized out>,
didwhat=<value optimized out>, k=<value optimized out>, conn=<value
optimized out>, data=<value optimized out>)
    at /Code/ThirdParty/curl/lib/transfer.c:613
#4 Curl_readwrite (conn=0x15ea3d0, done=0x15ea490 "") at
/Code/ThirdParty/curl/lib/transfer.c:1620
#5 0x00466ee4 in Transfer (conn=<value optimized out>) at
/Code/ThirdParty/curl/lib/transfer.c:1876
#6 Curl_perform (data=0x15d4670) at /Code/ThirdParty/curl/lib/transfer.c:2501
#############################

Looks like curl has received "404 Error:Page Not Found" response for
HTTP HEAD request from server. I have made few code changes in the
readwrite_data()
function in file transfer.c. The code changes are under "ISSHED start"
and "ISSHED stop" block. Here k->badheader is being set as
HEADER_ALLBAD. So I am returnig from here assuming this is an error
case.
Could you please review this and let me know if this is correct to
have this change or does it introduce any issues?

/*******************************************transfer.c***********************************************************/
/*
 * Go ahead and do a read if we have a readable socket or if
 * the stream was rewound (in which case we have data in a
 * buffer)
 */
static CURLcode readwrite_data(struct SessionHandle *data,
                               struct connectdata *conn,
                               struct SingleRequest *k,
                               int *didwhat, bool *done)
{
  CURLcode result = CURLE_OK;
  ssize_t nread; /* number of bytes read */
  bool is_empty_data = FALSE;
  *done = FALSE;

  /* This is where we loop until we have read everything there is to
     read or we get a EWOULDBLOCK */
  do {
    size_t buffersize = data->set.buffer_size?
      data->set.buffer_size : BUFSIZE;
    size_t bytestoread = buffersize;
    int readrc;

    if(k->size != -1 && !k->header) {
      /* make sure we don't read "too much" if we can help it since we
     might be pipelining and then someone else might want to read what
     follows! */
      curl_off_t totalleft = k->size - k->bytecount;
      if(totalleft < (curl_off_t)bytestoread)
    bytestoread = (size_t)totalleft;
    }

    if(bytestoread) {
      /* receive data from the network! */
      readrc = Curl_read(conn, conn->sockfd, k->buf, bytestoread, &nread);
      /* subzero, this would've blocked */
      if(0 > readrc)
    break; /* get out of loop */

      /* get the CURLcode from the int */
      result = (CURLcode)readrc;

      if(result>0)
    return result;
    }
    else {
      /* read nothing but since we wanted nothing we consider this an OK
     situation to proceed from */
      nread = 0;
    }

    if((k->bytecount == 0) && (k->writebytecount == 0)) {
#ifndef CURL_DISABLE_PROGRESS
      Curl_pgrsTime(data, TIMER_STARTTRANSFER);
#endif
      if(k->exp100 > EXP100_SEND_DATA)
    /* set time stamp to compare with when waiting for the 100 */
    k->start100 = Curl_tvnow();
    }

    *didwhat |= KEEP_READ;
    /* indicates data of zero size, i.e. empty file */
    is_empty_data = (bool)((nread == 0) && (k->bodywrites == 0));

    /* NUL terminate, allowing string ops to be used */
    if(0 < nread || is_empty_data) {
      k->buf[nread] = 0;
    }
    else if(0 >= nread) {
      /* if we receive 0 or less here, the server closed the connection
     and we bail out from this! */
      DEBUGF(infof(data, "nread <= 0, server closed connection, bailing\n"));
      k->keepon &= ~KEEP_READ;
      break;
    }

    /* Default buffer to use when we write the buffer, it may be changed
       in the flow below before the actual storing is done. */
    k->str = k->buf;

#ifndef CURL_DISABLE_HTTP
    /* Since this is a two-state thing, we check if we are parsing
       headers at the moment or not. */
    if(k->header) {
      /* we are in parse-the-header-mode */
      bool stop_reading = FALSE;
      result = readwrite_http_headers(data, conn, k, &nread, &stop_reading);
      if(result)
    return result;
      if(stop_reading)
    /* We've stopped dealing with input, get out of the do-while loop */
    break;
    }
#endif /* CURL_DISABLE_HTTP */

    /* This is not an 'else if' since it may be a rest from the header
       parsing, where the beginning of the buffer is headers and the end
       is non-headers. */
    if(k->str && !k->header && (nread > 0 || is_empty_data)) {
#ifndef CURL_DISABLE_HTTP
      if(0 == k->bodywrites && !is_empty_data) {
    /* These checks are only made the first time we are about to
       write a piece of the body */
    if(conn->protocol&PROT_HTTP) {
      /* HTTP-only checks */

      if(data->req.newurl) {
        if(conn->bits.close) {
          /* Abort after the headers if "follow Location" is set
         and we're set to close anyway. */
          k->keepon &= ~KEEP_READ;
          *done = TRUE;
          return CURLE_OK;
        }
        /* We have a new url to load, but since we want to be able
           to re-use this connection properly, we read the full
           response in "ignore more" */
        k->ignorebody = TRUE;
        infof(data, "Ignoring the response-body\n");
      }
      if(data->state.resume_from && !k->content_range &&
         (data->set.httpreq==HTTPREQ_GET) &&
         !k->ignorebody) {
        /* we wanted to resume a download, although the server doesn't
         * seem to support this and we did this with a GET (if it
         * wasn't a GET we did a POST or PUT resume) */
        failf(data, "HTTP server doesn't seem to support "
          "byte ranges. Cannot resume.");
        return CURLE_RANGE_ERROR;
      }

      if(data->set.timecondition && !data->state.range) {
        /* A time condition has been set AND no ranges have been
           requested. This seems to be what chapter 13.3.4 of
           RFC 2616 defines to be the correct action for a
           HTTP/1.1 client */
        if((k->timeofdoc > 0) && (data->set.timevalue > 0)) {
          switch(data->set.timecondition) {
          case CURL_TIMECOND_IFMODSINCE:
          default:
        if(k->timeofdoc < data->set.timevalue) {
          infof(data,
            "The requested document is not new enough\n");
          *done = TRUE;
          return CURLE_OK;
        }
        break;
          case CURL_TIMECOND_IFUNMODSINCE:
        if(k->timeofdoc > data->set.timevalue) {
          infof(data,
            "The requested document is not old enough\n");
          *done = TRUE;
          return CURLE_OK;
        }
        break;
          } /* switch */
        } /* two valid time strings */
      } /* we have a time condition */

    } /* this is HTTP */
      } /* this is the first time we write a body part */
#endif /* CURL_DISABLE_HTTP */
      k->bodywrites++;

      /* pass data to the debug function before it gets "dechunked" */
      if(data->set.verbose) {
    if(k->badheader) {
      Curl_debug(data, CURLINFO_DATA_IN, data->state.headerbuff,
             (size_t)k->hbuflen, conn);
      if(k->badheader == HEADER_PARTHEADER)
      {
          Curl_debug(data, CURLINFO_DATA_IN,
                  k->str, (size_t)nread, conn);
      }
      ////////ISSHED start/////////////////
      else if((k->badheader == HEADER_ALLBAD) && data->set.httpreq ==
HTTPREQ_HEAD)
      {
          printf("\nreadwrite_data========== If this is head \n");
          fflush(stdout);
          failf(data, "Received problem for head req");
          return CURLE_HTTP_RETURNED_ERROR;
      }
      ////////ISSHED stop/////////////////
    }
    else
      Curl_debug(data, CURLINFO_DATA_IN,
             k->str, (size_t)nread, conn);
      }

#ifndef CURL_DISABLE_HTTP
      if(k->chunk) {
    /*
     * Here comes a chunked transfer flying and we need to decode this
     * properly. While the name says read, this function both reads
     * and writes away the data. The returned 'nread' holds the number
     * of actual data it wrote to the client.
     */

    CHUNKcode res =
      Curl_httpchunk_read(conn, k->str, nread, &nread);

    if(CHUNKE_OK < res) {
      if(CHUNKE_WRITE_ERROR == res) {
        failf(data, "Failed writing data");
        return CURLE_WRITE_ERROR;
      }
      failf(data, "Received problem %d in the chunky parser", res);
      return CURLE_RECV_ERROR;
    }
    else if(CHUNKE_STOP == res) {
      size_t dataleft;
      /* we're done reading chunks! */
      k->keepon &= ~KEEP_READ; /* read no more */

      /* There are now possibly N number of bytes at the end of the
         str buffer that weren't written to the client.

         We DO care about this data if we are pipelining.
         Push it back to be read on the next pass. */

      dataleft = conn->chunk.dataleft;
      if(dataleft != 0) {
        infof(conn->data, "Leftovers after chunking. "
          " Rewinding %d bytes\n",dataleft);
        read_rewind(conn, dataleft);
      }
    }
    /* If it returned OK, we just keep going */
      }
#endif /* CURL_DISABLE_HTTP */

      if((-1 != k->maxdownload) &&
     (k->bytecount + nread >= k->maxdownload)) {
    /* The 'excess' amount below can't be more than BUFSIZE which
       always will fit in a size_t */
    size_t excess = (size_t)(k->bytecount + nread - k->maxdownload);
    if(excess > 0 && !k->ignorebody) {
      infof(data,
        "Rewinding stream by : %d"
        " bytes on url %s (size = %" FORMAT_OFF_T
        ", maxdownload = %" FORMAT_OFF_T
        ", bytecount = %" FORMAT_OFF_T ", nread = %d)\n",
        excess, data->state.path,
        k->size, k->maxdownload, k->bytecount, nread);
      read_rewind(conn, excess);
    }

    nread = (ssize_t) (k->maxdownload - k->bytecount);
    if(nread < 0 ) /* this should be unusual */
      nread = 0;

    k->keepon &= ~KEEP_READ; /* we're done reading */
      }

      k->bytecount += nread;
#ifndef CURL_DISABLE_PROGRESS
      Curl_pgrsSetDownloadCounter(data, k->bytecount);
#endif
      if(!k->chunk && (nread || k->badheader || is_empty_data)) {
    /* If this is chunky transfer, it was already written */
    if(k->badheader /* < HEADER_ALLBAD*/ && !k->ignorebody) {
      /* we parsed a piece of data wrongly assuming it was a header
         and now we output it as body instead */
      result = Curl_client_write(conn, CLIENTWRITE_BODY,
                     data->state.headerbuff,
                     k->hbuflen);
      if(result)
        return result;
    }
    if(k->badheader < HEADER_ALLBAD) {
      /* This switch handles various content encodings. If there's an
         error here, be sure to check over the almost identical code
         in http_chunks.c.
         Make sure that ALL_CONTENT_ENCODINGS contains all the
         encodings handled here. */
#ifdef HAVE_LIBZ
      switch (conn->data->set.http_ce_skip ?
          IDENTITY : k->content_encoding) {
      case IDENTITY:
#endif
        /* This is the default when the server sends no
           Content-Encoding header. See Curl_readwrite_init; the
           memset() call initializes k->content_encoding to zero. */
        if(!k->ignorebody)
          result = Curl_client_write(conn, CLIENTWRITE_BODY, k->str,
                     nread);
#ifdef HAVE_LIBZ
        break;

      case DEFLATE:
        /* Assume CLIENTWRITE_BODY; headers are not encoded. */
        if(!k->ignorebody)
          result = Curl_unencode_deflate_write(conn, k, nread);
        break;

      case GZIP:
        /* Assume CLIENTWRITE_BODY; headers are not encoded. */
        if(!k->ignorebody)
          result = Curl_unencode_gzip_write(conn, k, nread);
        break;

      case COMPRESS:
      default:
        failf (data, "Unrecognized content encoding type. "
           "libcurl understands `identity', `deflate' and `gzip' "
           "content encodings.");
        result = CURLE_BAD_CONTENT_ENCODING;
        break;
      }
#endif
    }
    k->badheader = HEADER_NORMAL; /* taken care of now */

    if(result)
      return result;
      }

    } /* if(! header and data to read ) */

    if(is_empty_data) {
      /* if we received nothing, the server closed the connection and we
     are done */
      k->keepon &= ~KEEP_READ;
    }

  } while(data_pending(conn));

  if(((k->keepon & (KEEP_READ|KEEP_WRITE)) == KEEP_WRITE) &&
     conn->bits.close ) {
    /* When we've read the entire thing and the close bit is set, the server
       may now close the connection. If there's now any kind of sending going
       on from our side, we need to stop that immediately. */
    infof(data, "we are done reading and this is set to close, stop send\n");
    k->keepon &= ~KEEP_WRITE; /* no writing anymore either */
  }

  return CURLE_OK;
}
-------------------------------------------------------------------
List admin: https://cool.haxx.se/list/listinfo/curl-users
FAQ: https://curl.haxx.se/docs/faq.html
Etiquette: https://curl.haxx.se/mail/etiquette.html
Received on 2016-07-22