Bugs of libf2c

translated on 2017/04/15 from Japanese page on 2016/09/10

The latest libf2c.zip available at Netlib f2c (dated 26 September 2013) has several bugs.


rsli.c: undefined behavior on READ statement in case of characters greater than 127

This bug is applicable to ISO-8859-x characters. (not limited to 2-byte CJK characters)

LineSource code to be amended
16-42
Original code
static int i_getc(Void)
{
	if(f__recpos >= f__svic->icirlen) {
		if (f__recpos++ == f__svic->icirlen)
			return '\n';
		z_rnew();
		}
	f__recpos++;
	if(f__icptr >= f__icend)
		return EOF;
	return(*f__icptr++);
	}

 static
#ifdef KR_headers
int i_ungetc(ch, f) int ch; FILE *f;
#else
int i_ungetc(int ch, FILE *f)
#endif
{
	if (--f__recpos == f__svic->icirlen)
		return '\n';
	if (f__recpos < -1)
		err(f__svic->icierr,110,"recend");
	/* *--icptr == ch, and icptr may point to read-only memory */
	return *--f__icptr /* = ch */;
	}
Amended
static int i_getc(Void)
{
	if(f__recpos >= f__svic->icirlen) {
		if (f__recpos++ == f__svic->icirlen)
			return '\n';
		z_rnew();
		}
	f__recpos++;
	if(f__icptr >= f__icend)
		return EOF;
	return((unsigned char)*f__icptr++); /* fix for 8bit char */
	}

 static
#ifdef KR_headers
int i_ungetc(ch, f) int ch; FILE *f;
#else
int i_ungetc(int ch, FILE *f)
#endif
{
	if (--f__recpos == f__svic->icirlen)
		return '\n';
	if (f__recpos < -1)
		err(f__svic->icierr,110,"recend");
	/* *--icptr == ch, and icptr may point to read-only memory */
	return (unsigned char)*--f__icptr /* = ch */;  /* fix for 8bit char */
	}

The implicit cast from signed char to signed integer produces a negative integer due to the declaration extern char *f__icptr; in case ch is greater than 127.

The result of this issue is system dependent on the implementation of `is-" macrofunction. For example in MS-VC++, Release build produces completely undefined behabior. Debug build produces an assersion at isdigit() next to i_getc() .

Changing to extern unsigned char *f__icptr; is not recommended because of old style programming of f2c.

NOTE: iio.c contains return(*(unsigned char *)f__icptr++); , i77vers.c contains follwoing comment. It might be a simple forgetting of rsli.c.

/* 24 Feb. 1995: iio.c: z_getc: insert (unsigned char *) to allow
		 internal reading of characters with high-bit set
		 (on machines that sign-extend characters). */

lread.c: array suffix overrun

LineSource code to be amended
54-64
Original code
char f__ltab[128+1] = {	/* offset one for EOF */
	0,
	0,0,AX,0,0,0,0,0,0,WH|B,SX|WH,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	SX|B|WH,0,AX,0,0,0,0,AX,0,0,0,SG,SX,SG,0,SX,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,EX,EX,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	AX,0,0,0,EX,EX,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};
Amended
char f__ltab[256+1] = {	/* offset one for EOF */ /* changed from 128 to 256 */
	0,
	0,0,AX,0,0,0,0,0,0,WH|B,SX|WH,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	SX|B|WH,0,AX,0,0,0,0,AX,0,0,0,SG,SX,SG,0,SX,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,EX,EX,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	AX,0,0,0,EX,EX,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};

Amendment of rsli.c leads to correct output of the macro GETC(ch) for charcode 0-255; However, original f__ltab[] has 128 contents and runs over.


lread.c: unavoidable memory leak

Line 493 in original lread.c lets the pointer f__lchar the address of allocated memory, however, the timing of `free' is not perfect and leads to a memory leak. This is unavoidable because of FORTRAN language's specification: "eating next lines when no suitable items is found".

This memory leak can be probed with MS-VC++ and following instructions.

Add to the beginning of f2c.h of libf2c source:

#ifdef _MSC_VER
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#endif

Add to the beginning of main.c of libf2c source:

#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#include <string.h>
#include <windows.h>

Change the definition of main() in main.c of libf2c source: original:

 int
#ifdef KR_headers
main(argc, argv) int argc; char **argv;
#else
main(int argc, char **argv)
#endif
{
xargc = argc;
xargv = argv;
modified:
 int
#ifdef KR_headers
main(argc, argv) int argc; char **argv;
#else
main(int argc, char **argv)
#endif
{
   _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

	_set_abort_behavior(0, _CALL_REPORTFAULT); // suppressing Dr.Watson clash dump
	_set_abort_behavior(1, _WRITE_ABORT_MSG); // enable error message to console (debug build only)

   _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
   _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
   _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);

   _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
   _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
   _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
xargc = argc;
xargv = argv;

Result:

Detected memory leaks!
Dumping objects ->
open.c(58) : {91} normal block at 0x00702868, 4096 bytes long.
 Data: < ]              > 20 5D 0A 00 20 20 20 20 20 20 20 20 20 20 20 20
lread.c(501) : {87} normal block at 0x006FD7D0, 128 bytes long.
 Data: <0.2             > 30 2E 32 00 CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.



about this site (in Japanese) (in English)