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()...