need suggestions for transparent input filter causing segfaults?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



hiho@ll

i just developed a simple input filter which seems to cause segfaults after about an hour of usage (apache v2.0.49)
what happens:
after apache restart everything works fine, but after about an hour i get segfaults on every request

what i need:
maybe someone has a few minutes and have a look at my module source in hope an expirienced developer spot's a bug

the the code should do:
i just want a transparent apache module which waits for a request of specific url which is configurable
i use a inputfilter, so it isn't necessary that the file really exists
in case the url equals the module configuration i write the header/post and get data in a file
afaik there are other modules which do similar stuff, but i just wanted to try it and i need a easy changeable file format (= my own)

what could be the problem:
i think of 3 possibilities
1. my brigade/bucket iteration for post data doesn't work correct
2. the module isn't really transparent (maybe the filter return values are a problem OR ap_get_brigade causes a problem in my handle_modulerequest function)
3. i have some very basic and trivial C bug (in this case, excuse for this mail ^^)

thx@ll for your time

the code:
#include "apr_strings.h"
#include "apr_user.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <strings.h>
#include <time.h>
#include <sys/file.h>

#define APR_WANT_STRFUNC
#include "apr_want.h"

#if APR_HAVE_UNISTD_H
#include <unistd.h>
#endif

#include "ap_config.h"
#include "httpd.h"
#include "http_config.h"
#include "http_request.h"

#define LOGLINE() fprintf(stderr, "%s:%d\n",__FILE__,__LINE__); fflush(stderr)
#define LOG(...) fprintf(stderr, "%s:%d ",__FILE__,__LINE__); fprintf(stderr,__VA_ARGS__); fflush(stderr)

// gcc -Wall -fPIC -c -I /usr/include/apache2 mod_mymodule.c
// gcc -shared -Wl,-soname,mod_mymodule.so.1 -o mod_mymodule.so mod_mymodule.o

#define TRDOMAIN 1
#define TRPATH 2
#define TRENABLE 3
#define TRLOCALPATH 4

static const char *mymoduleFilterName = "MYMODULE";

#define LOCALPATH "/tmp/mod_mymodule"

static int tr_domain = TRDOMAIN;
static int tr_path   = TRPATH;
static int tr_enable = TRENABLE;
static int tr_localpath = TRLOCALPATH;

#ifndef MAX_PATH
   #define MAX_PATH        1024
#endif

typedef struct {
    int enabled;
    char *moduledomain;
    char *modulepath;
    char *localpath;
} module_rec;

module AP_MODULE_DECLARE_DATA mymodule_module;

/* create a directory recursively */
static int recursivemkdir(char *path){
    if(path==NULL){ return 0; }  // if path is null, return error
    struct stat sb;
    if(stat(path,&sb)==0){ return 1; }    // if path exists, all ok
    if(mkdir(path,0775)==0){ return 1; }  // if mkdir doesnt fail, all ok
    if(errno!=ENOENT){ return 0; }        // if wrong errno, return error
    char *pos = strrchr(path,'/');
    if(pos==NULL){
       return 0;
    }
    *pos = '\0';  // try to create the most top level directory
    if(recursivemkdir(path)==0) return 0;  // failed, just return 0
    *pos = '/';   // if mkdir succeeded, try create the full path
    if(recursivemkdir(path)==0) return 0;  // failed, just return 0
    return 1;     // succeed!! directory created
}

/* write header data into file */
static int header_iterator(void *h,const char *key,const char *value){
    if(h==NULL){ return 1; }
    FILE *fp = (FILE*)h;
    fprintf(fp,"%s %s\n",key,value);
    return 1;
}

/* checks if request equals configuration and writes header,post,get as XML in file*/
static apr_status_t handle_modulerequest(ap_filter_t *f,
                                      apr_bucket_brigade *bb,
                                      ap_input_mode_t mode,
                                      apr_read_type_e block,
                                      apr_off_t readbytes)
{
    const char *str;
    apr_size_t length;
    apr_status_t finished = ap_get_brigade(f->next, bb, mode, block, readbytes);  // don't know, just copied from another module :)

#define RETFINISHED() { ap_remove_input_filter(f); return finished; }   // i have no idea if this is ok!?

    if(finished!=APR_SUCCESS) RETFINISHED();  // copied

    if(!f) RETFINISHED();   // just in case
    request_rec *r = f->r;
    if(!r || r->main) RETFINISHED(); // just in case

    module_rec *tcfg = ap_get_module_config(r->per_dir_config, &mymodule_module);  // get configuration
    if(!tcfg) RETFINISHED();   // just in case

    char *args="",*path_info="",*unparsed_uri="",*uri="";
    if(r->args){         args = r->args; }
    if(r->path_info){    path_info = r->path_info; }
    if(r->unparsed_uri){ unparsed_uri = r->unparsed_uri; }
    if(r->uri){          uri = r->uri; }
    const char *host = apr_table_get(r->headers_in, "Host");

    if(args && uri && host && tcfg->modulepath && tcfg->moduledomain && tcfg->localpath && strstr(args,"mymoduletest=doit")){
        LOG("MYMODULE: this is a test request{IST,SOLL}, ModulePath {'%s','%s'}, ModuleDomain {'%s','%s'}, GET {'%s'}, enabled: '%d', localpath {'%s'}\n",
            uri,tcfg->modulepath,host,tcfg->moduledomain,args,tcfg->enabled,tcfg->localpath);
        RETFINISHED();
    }

    // if not enabled or options set
    if(tcfg->enabled!=1 || !tcfg->moduledomain || !tcfg->modulepath) RETFINISHED();

    // if the request equals configuration
    if(uri && host && tcfg->modulepath && tcfg->moduledomain && strcasecmp(host,tcfg->moduledomain)==0 && strcasecmp(uri,tcfg->modulepath)==0){

      char dafilename[1025];
      memset(dafilename,0,sizeof(dafilename));

      if(args!=NULL && strlen(args)>0){

          char *begin = strstr(args,"dafilename");  // get file name from http get
          if(begin!=NULL){
             begin = strchr(begin,'=');
             char *end = strchr(begin,'&');
             if(begin!=NULL){
                 begin++;
                 int len = sizeof(dafilename)-1;
                 if(end!=NULL){
                     int len = sizeof(dafilename)-1<end-begin?sizeof(dafilename)-1:end-begin;
                     strncpy(dafilename,begin,len);
                 }
                 else{
                     strncpy(dafilename,begin,len);
                 }
             }
          }
      }
      // just creating path to directory
      if(strlen(dafilename)<=0){
          snprintf(dafilename,sizeof(dafilename)-1,"default");
      }
      char file[MAX_PATH];

      time_t tnow = time(NULL);
      struct tm now;
      localtime_r(&tnow,&now);
      strftime(file,sizeof(file)-1,tcfg->localpath,&now);

      // recursively create directory if necessary
      if(recursivemkdir(file)==0){
          char curdir[1024];
          memset(curdir,0,sizeof(curdir)-1);
          getcwd(curdir,sizeof(curdir)-1);
          LOG("MYMODULE: Couldn't create mymodule directory (%s, curdir %s), disabling module, %d:%s\n",tcfg->localpath,curdir,errno,strerror(errno));
          tcfg->enabled = 0;
          RETFINISHED();
      }

      // create tmp file
      strcat(file,"/");
      char tmpfile[MAX_PATH];
      snprintf(tmpfile,sizeof(tmpfile)-1,"%s%s.XXXXXX",file,dafilename);

      strcat(file,dafilename);
      int fd = mkstemp(tmpfile);

      if(fd!=-1){
         // open temporary xml file for writing
         FILE *fp = fdopen(fd,"a+");
         fprintf(fp,"<request>\n");
         fprintf(fp,"<date>%lu</date>\n",time(NULL));
         fprintf(fp,"<header>\n");
         // write header to file, does (void*)fp really work in every circumstances?
         apr_table_do(header_iterator,(void*)fp,r->headers_in,NULL);
         fprintf(fp,"</header>\n");
         fprintf(fp,"<param %s>",args);
         apr_bucket *bkt = NULL;
         // iterate through post data
         if(APR_BRIGADE_EMPTY(bb)) RETFINISHED();
         for(bkt=APR_BRIGADE_FIRST(bb);bkt!=APR_BRIGADE_SENTINEL(bb);bkt=APR_BUCKET_NEXT(bkt)){
            if (APR_BUCKET_IS_EOS(bkt)){ break; }
            if(apr_bucket_read(bkt, &str, &length, APR_BLOCK_READ)!=APR_SUCCESS || length<=0 || str==NULL){
                continue;
            }
            fwrite(str,sizeof(char),length,fp);
         }
         // do i have to delete or remove some bucket stuff here?

         fprintf(fp,"</param>\n");
         fprintf(fp,"</request>\n\n");
         LOGLINE();
         rewind(fp);
         char buffer[1024];
         int len = 0;
         // open destination file and append everything from temporary file
         FILE *dest = fopen(file,"a+");
         if(dest!=NULL){
            flock(fileno(dest),LOCK_EX);
            fseek(dest,0,SEEK_END);
            while(!feof(fp)){
               if((len=fread(buffer,sizeof(char),sizeof(buffer),fp))){
                   fwrite(buffer,sizeof(char),len,dest);
               }
            }
            flock(fileno(dest),LOCK_UN);
            fclose(dest);
         }
         fclose(fp);
         unlink(tmpfile);
      }

      return APR_EGENERAL;
    }
    RETFINISHED();
#undef RETFINISHED

}
// creating module configuration
static void* make_module_dir(apr_pool_t *p,char *d){
    module_rec *tcfg;
    if(!p){ return NULL; }

    tcfg = (module_rec *) apr_pcalloc(p, sizeof(module_rec));
    if(!tcfg){ return NULL; }
    tcfg->moduledomain = NULL;
    tcfg->modulepath = NULL;
    tcfg->enabled = 2;  // waiting for initializiation
    tcfg->localpath = LOCALPATH;

    return tcfg;
}
// set module configuration strings
static const char* set_module_strings(cmd_parms *cmd, void *mconfig, const char *name){
    if(!cmd || !mconfig || !name || !cmd->info){ return NULL; }
    if(strlen(name)<=0){
       return "Invalid Length of config option";
    }
    module_rec *tcfg = mconfig;
    char **offset = NULL;
    switch(*(int*)cmd->info){
       case TRDOMAIN:
                        offset = &tcfg->moduledomain;
                        break;
       case TRPATH:
                        offset = &tcfg->modulepath;
                        break;
       case TRLOCALPATH:
                        offset = &tcfg->localpath;
                        break;
    }
    if(offset!=NULL){
        (*offset) = apr_pstrdup(cmd->pool, name);
    }
    return NULL;
}
// set module enabled stuff
static const char* set_module_flags(cmd_parms *cmd, void *mconfig, int arg){
    if(!cmd || !mconfig || !cmd->info){ return NULL; }
    module_rec *tcfg = mconfig;
    switch(*(int*)cmd->info){
        case TRENABLE:
                       if(tcfg->enabled==2){
                           tcfg->enabled = arg;
                       }
                       break;
    }
    return NULL;
}

/* simple config options */
static const command_rec module_log_cmds[] = {
    AP_INIT_TAKE1("ModuleDomain", set_module_strings, &tr_domain, OR_FILEINFO,
                  "domain to which module applies"),
    AP_INIT_TAKE1("ModulePath", set_module_strings, &tr_path, OR_FILEINFO,
                  "Path for ModuleDomain to which module applies"),
    AP_INIT_TAKE1("ModuleLocalPath", set_module_strings, &tr_localpath, OR_FILEINFO,
                  "Path on local Filesystem where module will log tracked data"),
    AP_INIT_FLAG("Modulenable", set_module_flags, &tr_enable, OR_FILEINFO,
                 "whether or not to enable module"),
    {NULL}
};

static void register_hooks(apr_pool_t *p)
{
    ap_register_input_filter(mymoduleFilterName, handle_modulerequest, NULL,
                              AP_FTYPE_CONTENT_SET);
}

module AP_MODULE_DECLARE_DATA mymodule_module = {
    STANDARD20_MODULE_STUFF,
    make_module_dir,          /* dir config creater */
    NULL,                       /* dir merger --- default is to override */
    NULL,                       /* server config */
    NULL,                       /* merge server config */
    module_log_cmds,          /* command apr_table_t */
    register_hooks              /* register hooks */
};





an strace of the segfault process looks like this:
accept(3, {sa_family=AF_INET6, sin6_port=htons(4906), inet_pton(AF_INET6, "::ffff:192.168.11.200", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [68719476764]) = 8
getsockname(8, {sa_family=AF_INET6, sin6_port=htons(80), inet_pton(AF_INET6, "::ffff:192.168.11.77", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [68719476764]) = 0
fcntl(8, F_GETFL)                       = 0x2 (flags O_RDWR)
fcntl(8, F_SETFL, O_RDWR|O_NONBLOCK)    = 0
read(8, "GET /somefile.html?"..., 8000) = 531
stat("/srv/www/htdocs/somefile.html", 0x7fbfffed70) = -1 ENOENT (No such file or directory)
lstat("/srv", {st_mode=S_IFDIR|0755, st_size=96, ...}) = 0
lstat("/srv/www", {st_mode=S_IFDIR|0755, st_size=96, ...}) = 0
lstat("/srv/www/htdocs", {st_mode=S_IFDIR|0755, st_size=1448, ...}) = 0
lstat("/srv/www/htdocs/somefile.html", 0x7fbfffed60) = -1 ENOENT (No such file or directory)
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
chdir("/srv/www")                       = 0
rt_sigaction(SIGSEGV, {SIG_DFL}, {SIG_DFL}, 8) = 0
kill(3449, SIGSEGV)                     = 0
rt_sigreturn(0xd79)                     = 0
--- SIGSEGV (Segmentation fault) @ 0 (0) ---



Connect to the next generation of MSN Messenger  Get it now!

[Index of Archives]     [Open SSH Users]     [Linux ACPI]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Squid]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]

  Powered by Linux