fastcgi 更改环境变量environ引起的段错误(二)修改libfcgi库源码解决getenv段错误问题

关键词:fcgi、environ、getenv、段错误、segment fault、FCGI_Accept

fastcgi 更改环境变量environ引起的段错误(一)fcgi库代码分析

fastcgi 更改环境变量environ引起的段错误(二)修改libfcgi库源码解决getenv段错误问题
fastcgi 更改环境变量environ引起的段错误(三)getenv函数的使用

fastcgi 更改环境变量environ引起的段错误(四)gdb调试fcgi进程

上接fastcgi 更改环境变量environ引起的段错误(一)fcgi库代码分析-CSDN博客

让函数FCGI_Accept()不修改全局变量 extern char **envrion,而是创建一个新的全局变量 extern char ** mxl_environ,

在文件 libfcgi-2.4.2/include/fcgi_stdio.h 定义全局变量 DLLAPI extern char ** mxl_environ;

在文件 libfcgi-2.4.2/libfcgi/fcgi_stdio.c 新增变量extern char **mxl_environ:

extern char **mxl_environ;
int FCGI_Accept(void)
{
    if(!acceptCalled) {
        /*
         * First call to FCGI_Accept.  Is application running
         * as FastCGI or as CGI?
         */
        isCGI = FCGX_IsCGI();
        acceptCalled = TRUE;
        atexit(&FCGI_Finish);
    } else if(isCGI) {
        /*
         * Not first call to FCGI_Accept and running as CGI means
         * application is done.
         */
        return(EOF);
    }
    if(isCGI) {
        FCGI_stdin->stdio_stream = stdin;
        FCGI_stdin->fcgx_stream = NULL;
        FCGI_stdout->stdio_stream = stdout;
        FCGI_stdout->fcgx_stream = NULL;
        FCGI_stderr->stdio_stream = stderr;
        FCGI_stderr->fcgx_stream = NULL;
    } else {
        FCGX_Stream *in, *out, *error;
        FCGX_ParamArray envp;
        int acceptResult = FCGX_Accept(&in, &out, &error, &envp);
        if(acceptResult < 0) {
            return acceptResult;
        }
        FCGI_stdin->stdio_stream = NULL;
        FCGI_stdin->fcgx_stream = in;
        FCGI_stdout->stdio_stream = NULL;
        FCGI_stdout->fcgx_stream = out;
        FCGI_stderr->stdio_stream = NULL;
        FCGI_stderr->fcgx_stream = error;
        //environ = envp;	#不改变全局变量 extern char ** environ 的值
        mxl_environ = envp;
    }
    return 0;
}

在文件 libfcgi-2.4.2/cgi-fcgi/cgi-fcgi.c 中声明全局变量 char **mxl_environ;

在应用程序中模仿库文件glibc-2.32/stdlib/getenv.c 中的getenv函数,定义一个fcgi 专门使用的环境变量获取函数mxl_getenv,将全局全局变量 char **__environ 修改为 libfcgi 定义的全局变量 char **mxl_environ:

/* Return the value of the environment variable NAME.  This implementation
   is tuned a bit in that it assumes no environment variable has an empty
   name which of course should always be true.  We have a special case for
   one character names so that for the general case we can assume at least
   two characters which we can access.  By doing this we can avoid using the
   `strncmp' most of the time.  */
char *
mxl_getenv (const char *name)
{
  size_t len = strlen (name);
  char **ep;
  uint16_t name_start;

  if (mxl_environ == NULL || name[0] == '\0')
    return NULL;

  if (name[1] == '\0')
    {
      /* The name of the variable consists of only one character.  Therefore
	 the first two characters of the environment entry are this character
	 and a '=' character.  */
#if __BYTE_ORDER == __LITTLE_ENDIAN || !_STRING_ARCH_unaligned
      name_start = ('=' << 8) | *(const unsigned char *) name;
#else
      name_start = '=' | ((*(const unsigned char *) name) << 8);
#endif
      for (ep = mxl_environ; *ep != NULL; ++ep)
	{
#if _STRING_ARCH_unaligned
	  uint16_t ep_start = *(uint16_t *) *ep;
#else
	  uint16_t ep_start = (((unsigned char *) *ep)[0]
			       | (((unsigned char *) *ep)[1] << 8));
#endif
	  if (name_start == ep_start)
	    return &(*ep)[2];
	}
    }
  else
    {
#if _STRING_ARCH_unaligned
      name_start = *(const uint16_t *) name;
#else
      name_start = (((const unsigned char *) name)[0]
		    | (((const unsigned char *) name)[1] << 8));
#endif
      len -= 2;
      name += 2;

      for (ep = mxl_environ; *ep != NULL; ++ep)
	{
#if _STRING_ARCH_unaligned
	  uint16_t ep_start = *(uint16_t *) *ep;
#else
	  uint16_t ep_start = (((unsigned char *) *ep)[0]
			       | (((unsigned char *) *ep)[1] << 8));
#endif

	  if (name_start == ep_start && !strncmp (*ep + 2, name, len)
	      && (*ep)[len + 2] == '=')
	    return &(*ep)[len + 3];
	}
    }

  return NULL;
}

在应用程序中使用 mxl_getenv 替代函数 secure_getenv 来获取环境变量:

    /* 接收前端请求 */
    while (FCGI_Accept() >= 0) {
    pstReq->pcRequestMethod = mxl_getenv("REQUEST_METHOD"); //获取请求方法类型 POST/GET
    pstReq->pcDocumentUri = mxl_getenv("DOCUMENT_URI");     //获取uri字符串
    pstReq->pcContentType = mxl_getenv("CONTENT_TYPE");     //信息的MIME类型
    pstReq->pcQueryString = mxl_getenv("QUERY_STRING");     //URL地址中问号(?)之后的文本即为Query String
    pcContentLength = mxl_getenv("CONTENT_LENGTH");
    }

完整测试代码,实际测试中在函数pthread_run1()只执行  cp = getenv("LOCALDOMAIN");,把其它代码删除掉不会出现malloc 的段错误:

#define _GNU_SOURCE
#include <stdlib.h> 
#include "fcgi_stdio.h"
#include <stdbool.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

FILE *fp;


/* Return the value of the environment variable NAME.  This implementation
   is tuned a bit in that it assumes no environment variable has an empty
   name which of course should always be true.  We have a special case for
   one character names so that for the general case we can assume at least
   two characters which we can access.  By doing this we can avoid using the
   `strncmp' most of the time.  */
char *
mxl_getenv (const char *name)  // getenv函数在gblic库文件glibc-2.32/stdlib/getenv.c 
{
  size_t len = strlen (name);
  char **ep;
  uint16_t name_start;

  if (mxl_environ == NULL || name[0] == '\0')   //mxl_environ在 libfcgi 库中定义
    return NULL;

  if (name[1] == '\0')
    {
      /* The name of the variable consists of only one character.  Therefore
	 the first two characters of the environment entry are this character
	 and a '=' character.  */
#if __BYTE_ORDER == __LITTLE_ENDIAN || !_STRING_ARCH_unaligned
      name_start = ('=' << 8) | *(const unsigned char *) name;
#else
      name_start = '=' | ((*(const unsigned char *) name) << 8);
#endif
      for (ep = mxl_environ; *ep != NULL; ++ep)
	{
#if _STRING_ARCH_unaligned
	  uint16_t ep_start = *(uint16_t *) *ep;
#else
	  uint16_t ep_start = (((unsigned char *) *ep)[0]
			       | (((unsigned char *) *ep)[1] << 8));
#endif
	  if (name_start == ep_start)
	    return &(*ep)[2];
	}
    }
  else
    {
#if _STRING_ARCH_unaligned
      name_start = *(const uint16_t *) name;
#else
      name_start = (((const unsigned char *) name)[0]
		    | (((const unsigned char *) name)[1] << 8));
#endif
      len -= 2;
      name += 2;

      for (ep = mxl_environ; *ep != NULL; ++ep)
	{
#if _STRING_ARCH_unaligned
	  uint16_t ep_start = *(uint16_t *) *ep;
#else
	  uint16_t ep_start = (((unsigned char *) *ep)[0]
			       | (((unsigned char *) *ep)[1] << 8));
#endif

	  if (name_start == ep_start && !strncmp (*ep + 2, name, len)
	      && (*ep)[len + 2] == '=')
	    return &(*ep)[len + 3];
	}
    }

  return NULL;
}

void* pthread_run1(void* arg)
{
    char **ep;
    char *cp;

	while(1)
	{
        //setenv("mxl", "linfen", 1);
        //lt_setenv("mxl", "linfen");
        fprintf(fp,"I am thread 1,ID: 0x%lx.\n",pthread_self());
        fprintf(fp,"__environ = %p.\n", __environ);
        fflush(fp);
        
        cp = getenv("LOCALDOMAIN");
        if( cp == NULL)
            perror("get LOCALDOMAIN error:");
        else
            fprintf(fp, "env LOCALDOMAIN = %s.\n", cp);       

        for (ep = __environ; *ep != NULL; ++ep)  //在这里执行会段错误
        {
            fprintf(fp,"address of ep= %p , ep = %s\n", ep, *ep);
        }

        fprintf(fp,"====================================================\n");

        fflush(fp);
		sleep(5);   	
	}
}

FILE *fp;
int main(int iArgc, char *pcArgv[])
{
    pthread_t tid1;
    struct sigaction sa;
    char **ep;

    sa.sa_handler = SIG_IGN;
    sa.sa_flags = 0;
    if (sigemptyset(&sa.sa_mask) == -1 || //初始化信号集为空
            sigaction(SIGPIPE, &sa, 0) == -1) 
    { 
        perror("failed to ignore SIGPIPE; sigaction");
        exit(EXIT_FAILURE);
    }

    fp = fopen("/data/fcgi.log", "wb");

    fprintf(fp,"#########################\n");

    pthread_create(&tid1,NULL, pthread_run1,NULL);

#if 1
    /* 接收前端请求 */
    while (FCGI_Accept() >= 0) {
        fprintf(fp,"FCGI Accept \n"); 
        /*
        在libfcgi中使用自己创建的全局变量 char ** mxl_environ替代 environ,同时在FCGI_Accept接收到请求后使用函数mxl_getenv 替代getenv来获取环境变量。
        */
        fprintf(fp,"REQUEST_METHOD = %s.\n", mxl_getenv("REQUEST_METHOD")); 
        fprintf(fp,"DOCUMENT_URI = %s.\n", mxl_getenv("DOCUMENT_URI")); 
        fprintf(fp,"CONTENT_TYPE = %s.\n", mxl_getenv("CONTENT_TYPE")); 
        fprintf(fp,"SERVER_ADDR = %s.\n", mxl_getenv("SERVER_ADDR")); 
        fprintf(fp,"QUERY_STRING = %s.\n", mxl_getenv("QUERY_STRING")); 
        fprintf(fp,"main thread : __environ = %p.\n", __environ);
        for (ep = __environ; *ep != NULL; ++ep)
        {
            fprintf(fp,"main thread address of ep= %p , ep = %s\n", ep, *ep);
        }
        for (ep = mxl_environ; *ep != NULL; ++ep)
        {
            fprintf(fp,"main thread address of ep= %p , ep = %s\n", ep, *ep);
        }

        fprintf(fp,"********************************************\n");    
        fflush(fp);
    }
#else
    sleep(10);
    while (1) {
	sleep(1);
    }
#endif
    fclose(fp);
    return 0;
}/*End of Main*/

  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值