You are viewing a plain text version of this content. The canonical link for it is here.
Posted to modules-dev@httpd.apache.org by miim <xx...@yahoo.com.INVALID> on 2021/10/26 06:18:45 UTC

Chasing a segfault, part II

My thanks to everyone for their input on this problem.  While I was unable to get the backtrace and whatkilledus modules to report on failure, I isolated the cause to the following code in the handler.

The code itself does not segfault and indeed it appears to execute properly, retrieving the user-agent string with correct length and logging it.  However, when this code is included Apache segfaults some time later.  (It's not the logging causing it; the segfault still occurs without the logging.)

I can not see why this code should be overwriting Apache data structures in such a way as to cause Apache to segfault.  I have rewritten it several different ways and it still causes segfaults.

Might anyone have insight into this issue?


====================== HANDLER CODE ======================

static int bridcheck_handler
       (request_rec *r) {

  const char *ua_pointer;
  char useragent[UA_BUFFERSIZE];
  size_t ualength;
  size_t ualen2;

  /* Retrieve the user-agent string */

      /* Null the last byte in our buffer so that strings are always terminated */
  useragent[UA_BUFFERSIZE-1] = '\0';
      /* Load pointer to the Apache request record user-agent header field */
  ua_pointer = apr_table_get(r->headers_in, "User-Agent");
      /* Find out how long the Apache-supplied string is */
  ualength = strlen(ua_pointer);
      /* Copy only if there's something to copy */
  if (ualength != 0)
      /* Our buffer gets Apache's request record user-agent field */
      /* Protect from segfault by limiting length at buffersize -1 */
    strncpy(useragent, ua_pointer, UA_BUFFERSIZE-1);
      /* Don't use the original strncpy below.  Dissected the functionality into pieces above. */
      /*  strncpy(useragent, apr_table_get(r->headers_in, "User-Agent"), UA_BUFFERSIZE-1); */
      /* Now that we have our prize ... how long is it? */
  ualen2 = strlen(useragent);

  ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r,
                  "bc: ualength = %u, <%s>",
                  ualength, apr_table_get(r->headers_in, "User-Agent"));
  ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r,
                  "    ualen2   = %u, <%s>",
                  ualen2, useragent);

  return DECLINED;

  }

====================== ERROR LOG EXTRACT ====================

Oct 26 05:10:39  157.55.39.67  bc: ualength = 71, <Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)>
Oct 26 05:10:39  157.55.39.67      ualen2   = 71, <Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)>
Oct 26 05:11:11  71.6.232.7  bc: ualength = 115, <Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36>
Oct 26 05:11:11  71.6.232.7      ualen2   = 115, <Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36>
Oct 26 05:17:33  110.70.47.92  bc: ualength = 120, <Mozilla/5.0 (Linux; Android 9; SM-G955N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.85 Mobile Safari/537.36>
Oct 26 05:17:33  110.70.47.92      ualen2   = 120, <Mozilla/5.0 (Linux; Android 9; SM-G955N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.85 Mobile Safari/537.36>

Re: Chasing a segfault, part II

Posted by miim <xx...@yahoo.com.INVALID>.
 

After two days of testing I can testify that Sorin's analysis is correct. Sometimes Apache points to a user-agent string, sometimes it points to a null string, and sometimes there is a null pointer.

The reason nothing appeared in the logs is because the requests coming in with empty user-agent headers were going to the default server, not to one of the named vhosts. The default server returns 410 on everything and logs nothing. The test module should not have been active on the default server, but it is a good thing it was; this would have been wretched if it had gone to the field with that bug.

For future reference to anyone dealing with the user-agent string, here is the code I adopted which handles all three possibilities. It is streamlined from the original version and no longer copies the user-agent string to local storage. This allows the CRC and DJB2 routines to process the entire string and not a length-limited substring.


size_t ualength;
const unsigned char* uastring;

...
...

 /* Retrieve the user-agent string */

 uastring = apr_table_get(r->headers_in, "User-Agent");

 /* If there is no user-agent string, the CRC/DJB2 defaults to xFFFFFFFF/x0 */

 ua_crc = 0xFFFFFFFF;
 ua_djb = 0x0;

 /* If the user-agent string is empty, the CRC/DJB2 defaults to x0/xFFFFFFFF. */

 if (uastring != NULL) {
 ua_crc = 0x0;
 ua_djb = 0xFFFFFFFF;
 ualength = strlen(uastring);

 /* If user-agent string exists, compute the CRC-32 and DJB2 hash */

 if (ualength != 0) {
 ua_crc = bc_crc_32(r, bc_scfg, uastring, ualength);
 ua_djb = bc_djb2hash(r, bc_scfg, uastring, ualength);
 }
 }


My thanks to everyone who commented on the problem. I would not have found the issue without assistance.

     On Tuesday, 26 October 2021, 07:59:04 am GMT+1, Sorin Manolache <so...@gmail.com> wrote:  
 
 On 26/10/2021 08.18, miim wrote:
> ua_pointer = apr_table_get(r->headers_in, "User-Agent");
>        /* Find out how long the Apache-supplied string is */
>    ualength = strlen(ua_pointer);

If the request does not contain any user-agent then ua_pointer will be 
NULL. strlen of NULL will segfault.

S
  

Re: Chasing a segfault, part II

Posted by Sorin Manolache <so...@gmail.com>.
On 26/10/2021 08.18, miim wrote:
> ua_pointer = apr_table_get(r->headers_in, "User-Agent");
>        /* Find out how long the Apache-supplied string is */
>    ualength = strlen(ua_pointer);

If the request does not contain any user-agent then ua_pointer will be 
NULL. strlen of NULL will segfault.

S