You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by TO...@aol.com on 1999/01/25 23:34:52 UTC

WIN32 CGI - 16 BIT UNSTABLE - 1 OF 4

WIN32 PROBLEM: 16-BIT EXECUTABLE CGI HANDLING STILL UNSTABLE

RELATES TO: A number of currently open Apache WIN32 CGI PR REPORTS

Possibly relates to all PR's that concern CGI error(s)
'Server Internal Error'
'File not found: Could not spawn'
etc.

The following is from the latest version of

..\APACHE_1.3.4\SRC\MAIN\UTIL_SCRIPT.C

My comments are all preceded with //:

The USC 'Berky-jerky' coding style has been re-organized
to make the code easier to follow...

API_EXPORT(int) ap_call_exec( request_rec *r,
                              child_info  *pinfo,
                              char        *argv0,
                              char        **env,
                              int         shellcmd
                            )
{
//: Default 'pid' value has changed for WIN32 vers 1.3.4.
//: WIN32 section will change default 'pid' to -1.

int pid = 0;

//: Top leg includes handling of all the RLIMIT_XXXX flags...

#ifdef OS2

//: OS2 handler is in this leg...

#elif defined(WIN32)
    {
    //: A lot of stack variables declared here...
    //: The only relevant ones for this discussion are...

	int is_script = 0;
	int is_binary = 0;
    int is_exe    = 0;
	char interpreter[2048];	/* hope it's enough for the interpreter path */
	interpreter[0] = 0;
	pid = -1;

        quoted_filename = ap_pstrcat(r->pool, "\"", r->filename, "\"", NULL);

        if (!shellcmd)
          {
           exename = strrchr(r->filename, '/');

           if (!exename)
             {
              exename = strrchr(r->filename, '\\');
             }

           if (!exename)
             {
              exename = r->filename;
             }
           else
             {
              exename++;
             }
           dot = strrchr(exename, '.');

           if (dot)
             {
              if (   !strcasecmp(dot, ".BAT")
                  || !strcasecmp(dot, ".CMD")
                  || !strcasecmp(dot, ".EXE")
                  || !strcasecmp(dot, ".COM"))
                {
                 is_exe = 1;
                }
             }

             if (!is_exe)
               {
                //: We enter here is the 'filename' does not contain
                //: one of the HARD-CODED 'recognizable' executable
                //: extensions...

                //: We must now try to 'open' it and figure out
                //: what it is...

                program = fopen(r->filename, "rb");

                if (!program)
                  {
                   //: If we can't open the filename then it's
                   //: Goodnight Gracie... log error and bail...

                   ap_log_rerror( APLOG_MARK, APLOG_ERR, r,
                                 "fopen(%s) failed", r->filename);

                   //: Turn and burn >> 'pid' is still '-1' at this point...
                   //: NOTE: Versions prior to 1.3.4 return '0', not '-1'

                   return (pid);
                  }

                //: File is now open. Read in 2047 bytes ( 2048 -1 )...

                //: NOTE: Prior to version 1.3.4 this was reading in
                //:       'sizeof( interpreter )'. The '-1' is new...
                //:       and will prevent the STACK OVERFLOW seen in
                //:       all versions prior to 1.3.4. The STACK OVERFLOW
                //:       prior to 1.3.4 could have been responsible for
                //:       any number of WIN32 CGI PR's especially those
                //:       regarding 'Server dies after multiple CGI's'.

                sz = fread(interpreter, 1, sizeof(interpreter) - 1, program);

                if (sz < 0)
                  {
                   //: If there was a legitimate ERROR reading file then
                   //: Goodnight Gracie... log error and bail...

                   //: NOTE that this logic handles NEGATIVE error
                   //: values only. If the file exists but is EMPTY
                   //: (sz=0) we fall on through without catching it
                   //: and it's left up to the string parsers below
                   //: to sense that something is wrong...

                    ap_log_rerror( APLOG_MARK, APLOG_ERR, r,
                                   "fread of %s failed", r->filename);

                    fclose(program);

                    //: Turn and burn >> 'pid' is still '-1' at this point...
                    //: NOTE: Versions prior to 1.3.4 return '0', not '-1'

                    return (pid);
                   }

                //: We are now about to add a NULL to the end of the
                //: data read from the file so the upcoming 'str' calls
                //: don't crash...

                //: NOTE: Now that we are reading 'sizeof(interpreter)-1'
                //:       bytes the STACK OVERWRITE bug surrounding the
                //:       adding of a NULL to the end of the buffer is
                //:       no longer present. Anything prior to 1.3.4
                //:       still has the STACK OVERWRITE bug at this point.
                //:

                interpreter[sz] = 0;

                fclose(program);

                //: Time to check for EL-BANGO (SHA-BANG)...

                if (!strncmp(interpreter, "#!", 2))
                  {
                   //: If EL-BANGO is 'seen' then assume it's a script...

                   is_script = 1;

                   //: The rest of this pickup simply accepts
                   //: all characters following EL-BANGO up
                   //: to the first CR or LF as the INTERPRETER
                   //: that should be run for this script...
                   //: Could be ANYTHING here. No checks are made.

                   //: This is actually a BIG SECURITY HOLE but that's
                   //: another story for another time...

                   for (i = 2; i < sizeof(interpreter); i++)
                      {
                       if   ((interpreter[i] == '\r')
                          || (interpreter[i] == '\n'))
                         {
                          break;
                         }
                      }

                   interpreter[i] = 0;

                   //: Next 2 lines just remove leading spaces
                   //: from upcoming command line and left-shifts
                   //: the buffer. Still no checks being made on
                   //: the content of the line that will execute...

                   //: Head 'em up...

                   for (i = 2; interpreter[i] == ' '; ++i)
                        ;

                   //: Move 'em out...

                   memmove(interpreter+2,interpreter+i,strlen(interpreter+i)+1
);
                  }
                else
                  {
                   //: The first 2 bytes of the file are NOT EL-BANGO...

                   /* Check to see if it's a executable */

                   //: IMAGE_DOS_HEADER and
                   //: IMAGE_DOS_SIGNATURE are NOT part of Apache...

                   //: They are MSVC compiler specific and come from
                   //: X:\MSVC5\VC\INCLUDE\WINNT.H

                   //: The use of these 'define's without encapsulation
                   //: in '#ifdef _MSVC' means that other 'C' compilers
                   //: will fail if they don't have the same 'define's.

                   //: Apache only uses MSVC and does not 'officially'
                   //: support any other WIN32 'C' compiler at this time
                   //: so there are currently no PR's about this.

                   //: Lay the DOS EXECUTABLE IMAGE PREAMBLE structure
                   //: template on top of the data read from the
                   //: beginning of the file in question...

                   //: This PREAMBLE ( or header, if you prefer )
                   //: must be a multiple of 16 byes in length, and
                   //: is nearly always set at 512 bytes length for
                   //: NO REAL REASON.

                   //: This bears repeating: Length is USUALLY 512 bytes
                   //: but there has never been any REAL REASON for it.
                   //: It is NOT 'fixed in stone'. Never has been.
                   //: The PREAMBLE is designed to be VARIABLE LENGTH.

                   //: It enables a program (.COM or .EXE) to break
                   //: the 64K boundary because it contains the famous
                   //: 'relocation table' which is a table of segment
                   //: pointers within the program which must be 'fixed'
                   //: at load time to point to the correct physical
                   //: segment. To these we add the actual physical
                   //: segment at which the program was loaded (the segment
                   //: segment immediately after the PSP).

                   IMAGE_DOS_HEADER *hdr = (IMAGE_DOS_HEADER*)interpreter;

                   //: IMAGE_DOS_SIGNATURE is a 2-byte define that
                   //: equates to the old-as-dirt DOS EXECUTABLE IMAGE
                   //: signature 'MZ' which stands for Mark Zbikowski,
                   //: one of the early authors of DOS ( circa v 2.0 ).

                   //: As it is currently coded...
                   //: The ONLY WAY we will 'recognize' this file as a
                   //: DOS EXECUTABLE is if BOTH of the following are TRUE...

                   //: 1. The first 2 bytes of the file are MZ ( Actually
                   //:    'ZM' since executables use LSB/MSB byte coding.

                   //: 2. The SECOND FIELD of the PREAMBLE evaluates to
                   //:    some amount of bytes LESS than 512.

                   //: This is PROBLEM and a MYSTERY.

                   //: Structure element member 'e->cblp'
                   //: normally represents the 'remainder' of
                   //: the 'number of pages'. There is no comment
                   //: here as to why this is invloved and it is
                   //: the source of the existing problem.

                   //: It is perfectly possible for a DOS 16-bit
                   //: .EXE signature to have BYTES at offset(s)
                   //: 2-3 which will evaluate to something MORE
                   //: than 512 bytes. It is product and compiler
                   //: dependent. Use EXEDUMP or any HEX editor
                   //: to examine 16-bit and 32-bit .EXE and .COM
                   //: files and it is easy to see that it's a
                   //: MISTAKE to RELY on the 'LESS THAN 512' condition.

                   //: There are other 'magic' signatures that can/should
                   //: be used here that are more reliable. See
                   //: accompanying documentation regarding USC TC5
                   //: pickup code that accomplishes the same thing
                   //: being attempted here but does it in a more
                   //: reliable way.

                   if ( hdr->e_magic == IMAGE_DOS_SIGNATURE &&
                        hdr->e_cblp   < 512 )
                     {
                      //: A LOT of 16-bit and 'alternate C compiler'
                      //: executables will FAIL this test because
                      //: their bytes at offset(s) 2-3 will evaluate
                      //: to something GREATER than 512 bytes.

                      //: See documentation reagarding 'MZ' header...

                      is_binary = 1;
                     }

                   //: End of 'else'... EL-BANGO '#!' was NOT SEEN...
                  }

                //: End 'if( !is_exe )' which means the file did NOT
                //: have one of the HARD-CODED executable file extensions.
               }

            /* Bail out if we haven't figured out what kind of
             * file this is by now..
             */

            if ( !is_exe && !is_script && !is_binary )
              {
               //: If we haven't passed ANY of the 'tests' then it's
               //: Goodnight Gracie... log error and bail...

               ap_log_rerror(
               APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r,
               "%s is not executable; ensure interpreted scripts have "
               "\"#!\" first line",
               r->filename);

               //: Turn and burn >> 'pid' is still '-' at this point...
               //: NOTE: Versions prior to 1.3.4 return '0', not '-1'

               return (pid);
              }

         //: End if( !shellcmd )' pickup section...
        }

    //: The rest... launch child... pass args... etc...

    //: If WIN32 this could still be '-1'...
    //: If NOT WIN32 then this could still be '0'...

    return (pid);
    }

}// End of ap_call_exec()...