NetBSD Problem Report #1862

From gnats  Wed Dec 27 19:31:53 1995
Received: (from jtc@localhost) by pain.lcs.mit.edu (8.6.12/8.6.9) id TAA04634; Wed, 27 Dec 1995 19:31:53 -0500
Message-Id: <199512280031.TAA04634@pain.lcs.mit.edu>
Date: Wed, 27 Dec 1995 19:31:53 -0500
From: "J.T. Conklin" <jtc>
Reply-To: jtc@wimsey.com
To: sun-lamp-gnats
Subject: printf/scanf don't support X/Open positional parameters
X-Send-Pr-Version: 3.95

>Number:         1862
>Category:       lib
>Synopsis:       printf/scanf don't support X/Open positional parameters
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kleink
>State:          closed
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed Dec 27 19:35:23 +0000 1995
>Closed-Date:    Sun Jun 26 06:49:48 +0000 2016
>Last-Modified:  Sun Jun 26 06:49:48 +0000 2016
>Originator:     J.T. Conklin
>Release:        1.1
>Organization:

>Environment:

System: NetBSD pain.lcs.mit.edu 1.0A NetBSD 1.0A (PAIN) #0: Sat Oct 7 08:07:40 EDT 1995 mycroft@pain.lcs.mit.edu:/a/users/mycroft/sys/arch/i386/compile/PAIN i386


>Description:
Printf and scanf do not support the X/Open positional parameters.  This
makes message catalogs much less useful than they could be.

>How-To-Repeat:
Use code that uses this feature.

>Fix:
Add support to printf/scanf in a efficent manner.

>Release-Note:
>Audit-Trail:

Responsible-Changed-From-To: lib-bug-people->jtc
Responsible-Changed-By: jtc
Responsible-Changed-When: Thu Dec 28 14:10:57 1995
Responsible-Changed-Why:

Responsible-Changed-From-To: jtc->lib-bug-people 
Responsible-Changed-By: fair 
Responsible-Changed-When: Thu Jun 17 14:37:47 PDT 1999 
Responsible-Changed-Why:  
jtc's attentions are elsewhere - someone else should pick this up. 
(if I read this right, it's an i18n problem) 
Responsible-Changed-From-To: lib-bug-people->kleink 
Responsible-Changed-By: lukem 
Responsible-Changed-When: Wed Apr 18 05:03:00 PDT 2001 
Responsible-Changed-Why:  
klaus is our standards guru; what are his thoughts? 

From: Klaus Klein <kleink@er.reziprozitaet.de>
To: lukem@netbsd.org
Cc: gnats-bugs@netbsd.org, lib-bug-people@netbsd.org
Subject: Re: lib/1862
Date: 18 Apr 2001 14:36:34 +0200

 These are indeed extensions defined in XPG4; Erik's comment about i18n
 is likely to have been guided by that these positional parameters can
 be handy when doing i18n work. They're one of the reasons why I added
 va_copy.

 I can look after this after the specified-width integer rototill,
 which affects printf/scanf as well.


 - Klaus

From: David Laight <david@l8s.co.uk>
To: gnats-bugs@gnats.netbsd.org
Subject: re: lib/1862: printf/scanf don't support X/Open positional parameters
Date: Mon, 1 Sep 2003 15:50:02 +0100

 I've just sat down and implemented this for printf.

 However there are definite security problems lurking for scanf and
 even parts of printf.

 Consider the following eror message:

 	printf("value %d unexpected\n", n);

 Now if I have control over the format string and the value of 'n' I can
 write a small integer to any location in the program by making 'n' its
 address and the format "xxxx%n".

 Without control of the data "xxxx%<argnum>$n..." could be arranged to
 overwrite via ANY pointer on the callers stack.

 Similarly use of %<argnum>$ in a scanf format string could overwrite
 the value read using any on-stack pointer.

 These overwrites can all be done with standard format strings, but the
 %n$ variants make it so much easier!

 Anyway the patch is below.
 It allows upto 1024 arguments, but TOG say that NL_ARGMAX need only be 9.
 Assuming a single digit would simplify some of the code, and a small number
 does reduce the opportunity for locating a useful on-stack pointer.

 	David

 Index: vfprintf.c
 ===================================================================
 RCS file: /cvsroot/src/lib/libc/stdio/vfprintf.c,v
 retrieving revision 1.45
 diff -u -p -r1.45 vfprintf.c
 --- vfprintf.c	2003/08/07 16:43:34	1.45
 +++ vfprintf.c	2003/09/01 14:43:02
 @@ -65,9 +65,20 @@ __RCSID("$NetBSD: vfprintf.c,v 1.45 2003
  #include "fvwrite.h"
  #include "extern.h"

 +#ifdef FLOATING_POINT
 +#define POSITIONAL_PARAMS
 +#endif
 +
  static int __sprint __P((FILE *, struct __suio *));
  static int __sbprintf __P((FILE *, const char *, va_list)) 
       __attribute__((__format__(__printf__, 2, 0)));
 +#ifdef POSITIONAL_PARAMS
 +static void *set_printf_args(const char *, _BSD_VA_LIST_);
 +static uintmax_t get_arg_int(void *, int);
 +static double get_arg_double(void *, int);
 +static void *get_arg_ptr(void *, int);
 +static void free_parg(void *);
 +#endif

  /*
   * Flush out all the vectors defined by the given uio,
 @@ -187,6 +198,11 @@ __UNCONST(v)
  #define	SIZEINT		0x200		/* (signed) size_t */
  #define	ZEROPAD		0x400		/* zero (as opposed to blank) pad */
  #define FPT		0x800		/* Floating point number */
 +#ifdef POSITIONAL_PARAMS
 +#define POINTER		0x10000		/* Pointer to something */
 +#define SIGNED		0x20000		/* signed value */
 +#define CHARINT		0x40000		/* move above when %hhd implemented */
 +#endif

  int
  vfprintf(fp, fmt0, ap)
 @@ -264,11 +280,16 @@ vfprintf_unlocked(fp, fmt0, ap)
  	 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
  	static const char zeroes[PADSIZE] =
  	 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
 +#ifdef POSITIONAL_PARAMS	/* %nnn$... */
 +	void *parg = NULL;
 +	int argnum = 0;
 +	char *ncp;
 +#endif

  	/*
  	 * BEWARE, these `goto error' on error, and PAD uses `n'.
  	 */
 -#define	PRINT(ptr, len) { \
 +#define	PRINT(ptr, len) do { \
  	iovp->iov_base = __UNCONST(ptr); \
  	iovp->iov_len = (len); \
  	uio.uio_resid += (len); \
 @@ -278,8 +299,9 @@ vfprintf_unlocked(fp, fmt0, ap)
  			goto error; \
  		iovp = iov; \
  	} \
 -}
 -#define	PAD(howmany, with) { \
 +} while (/*CONSTCOND*/0)
 +
 +#define	PAD(howmany, with) do { \
  	if ((n = (howmany)) > 0) { \
  		while (n > PADSIZE) { \
  			PRINT(with, PADSIZE); \
 @@ -287,19 +309,20 @@ vfprintf_unlocked(fp, fmt0, ap)
  		} \
  		PRINT(with, n); \
  	} \
 -}
 -#define	FLUSH() { \
 +} while (/*CONSTCOND*/0)
 +
 +#define	FLUSH() do { \
  	if (uio.uio_resid && __sprint(fp, &uio)) \
  		goto error; \
  	uio.uio_iovcnt = 0; \
  	iovp = iov; \
 -}
 +} while (/*CONSTCOND*/0)

  	/*
  	 * To extend shorts properly, we need both signed and unsigned
  	 * argument extraction methods.
  	 */
 -#define	SARG() \
 +#define	SARG(flags, ap) \
  	(flags&MAXINT ? va_arg(ap, intmax_t) : \
  	    flags&PTRINT ? va_arg(ap, ptrdiff_t) : \
  	    flags&SIZEINT ? va_arg(ap, ssize_t) : /* XXX */ \
 @@ -307,7 +330,7 @@ vfprintf_unlocked(fp, fmt0, ap)
  	    flags&LONGINT ? va_arg(ap, long) : \
  	    flags&SHORTINT ? (long)(short)va_arg(ap, int) : \
  	    (long)va_arg(ap, int))
 -#define	UARG() \
 +#define	UARG(flags, ap) \
  	(flags&MAXINT ? va_arg(ap, uintmax_t) : \
  	    flags&PTRINT ? va_arg(ap, uintptr_t) : /* XXX */ \
  	    flags&SIZEINT ? va_arg(ap, size_t) : \
 @@ -383,13 +406,24 @@ reswitch:	switch (ch) {
  			flags |= ALT;
  			goto rflag;
  		case '*':
 +#ifdef POSITIONAL_PARAMS
 +			if (parg != NULL) {
 +				n = strtoul(fmt, &ncp, 10);
 +				fmt = ncp;
 +				ch = *fmt++;
 +				if (ch != '$')
 +					goto reswitch;
 +				width = (int)get_arg_int(parg, n);
 +			} else
 +#endif
 +			width = va_arg(ap, int);
  			/*
  			 * ``A negative field width argument is taken as a
  			 * - flag followed by a positive field width.''
  			 *	-- ANSI X3J11
  			 * They don't exclude field widths read from args.
  			 */
 -			if ((width = va_arg(ap, int)) >= 0)
 +			if (width >= 0)
  				goto rflag;
  			width = -width;
  			/* FALLTHROUGH */
 @@ -401,6 +435,16 @@ reswitch:	switch (ch) {
  			goto rflag;
  		case '.':
  			if ((ch = *fmt++) == '*') {
 +#ifdef POSITIONAL_PARAMS
 +				if (parg != NULL) {
 +					n = strtoul(fmt, &ncp, 10);
 +					fmt = ncp;
 +					ch = *fmt++;
 +					if (ch != '$')
 +						goto reswitch;
 +					n = (int)get_arg_int(parg, n);
 +				} else
 +#endif
  				n = va_arg(ap, int);
  				prec = n < 0 ? -1 : n;
  				goto rflag;
 @@ -427,6 +471,14 @@ reswitch:	switch (ch) {
  				n = 10 * n + to_digit(ch);
  				ch = *fmt++;
  			} while (is_digit(ch));
 +#ifdef POSITIONAL_PARAMS
 +			if (ch == '$') {
 +				if (parg == NULL)
 +					parg = set_printf_args(fmt0, ap);
 +				argnum = n;
 +				goto rflag;
 +			}
 +#endif
  			width = n;
  			goto reswitch;
  #ifdef FLOATING_POINT
 @@ -458,6 +510,11 @@ reswitch:	switch (ch) {
  			flags |= SIZEINT;
  			goto rflag;
  		case 'c':
 +#ifdef POSITIONAL_PARAMS
 +			if (parg != NULL)
 +				*buf = (int)get_arg_int(parg, argnum);
 +			else
 +#endif
  			*buf = va_arg(ap, int);
  			cp = buf;
  			size = 1;
 @@ -468,7 +525,12 @@ reswitch:	switch (ch) {
  			/*FALLTHROUGH*/
  		case 'd':
  		case 'i':
 -			_uintmax = SARG();
 +#ifdef POSITIONAL_PARAMS
 +			if (parg != NULL)
 +				_uintmax = get_arg_int(parg, argnum);
 +			else
 +#endif
 +			_uintmax = SARG(flags, ap);
  			if ((intmax_t)_uintmax < 0) {
  				_uintmax = -_uintmax;
  				sign = '-';
 @@ -488,6 +550,11 @@ reswitch:	switch (ch) {
  				prec = 1;
  			}

 +#ifdef POSITIONAL_PARAMS
 +			if (parg != NULL)
 +				_double = get_arg_double(parg, argnum);
 +			else
 +#endif
  			if (flags & LONGDBL) {
  				_double = (double) va_arg(ap, long double);
  			} else {
 @@ -549,6 +616,15 @@ reswitch:	switch (ch) {
  			break;
  #endif /* FLOATING_POINT */
  		case 'n':
 +#ifdef POSITIONAL_PARAMS
 +			if (parg != NULL)
 +				/*
 +				 * This could be implemented, but it stinks
 +				 * of a security risk.
 +				 */
 +				PRINT("(%n unassigned)", 15);
 +			else
 +#endif
  			if (flags & MAXINT)
  				*va_arg(ap, intmax_t *) = ret;
  			else if (flags & PTRINT)
 @@ -568,7 +644,12 @@ reswitch:	switch (ch) {
  			flags |= LONGINT;
  			/*FALLTHROUGH*/
  		case 'o':
 -			_uintmax = UARG();
 +#ifdef POSITIONAL_PARAMS
 +			if (parg != NULL)
 +				_uintmax = get_arg_int(parg, argnum);
 +			else
 +#endif
 +			_uintmax = UARG(flags, ap);
  			base = OCT;
  			goto nosign;
  		case 'p':
 @@ -579,15 +660,26 @@ reswitch:	switch (ch) {
  			 * defined manner.''
  			 *	-- ANSI X3J11
  			 */
 +#ifdef POSITIONAL_PARAMS
 +			if (parg != NULL)
 +				_uintmax = (uintptr_t)get_arg_ptr(parg, argnum);
 +			else
 +#endif
  			/* NOSTRICT */
 -			_uintmax = (u_long)va_arg(ap, void *);
 +			_uintmax = (uintptr_t)va_arg(ap, void *);
  			base = HEX;
  			xdigs = "0123456789abcdef";
  			flags |= HEXPREFIX;
  			ch = 'x';
  			goto nosign;
  		case 's':
 -			if ((cp = va_arg(ap, char *)) == NULL)
 +#ifdef POSITIONAL_PARAMS
 +			if (parg != NULL)
 +				cp = get_arg_ptr(parg, argnum);
 +			else
 +#endif
 +			cp = va_arg(ap, char *);
 +			if (cp == NULL)
  				cp = "(null)";
  			if (prec >= 0) {
  				/*
 @@ -611,7 +703,12 @@ reswitch:	switch (ch) {
  			flags |= LONGINT;
  			/*FALLTHROUGH*/
  		case 'u':
 -			_uintmax = UARG();
 +#ifdef POSITIONAL_PARAMS
 +			if (parg != NULL)
 +				_uintmax = get_arg_int(parg, argnum);
 +			else
 +#endif
 +			_uintmax = UARG(flags, ap);
  			base = DEC;
  			goto nosign;
  		case 'X':
 @@ -619,7 +716,13 @@ reswitch:	switch (ch) {
  			goto hex;
  		case 'x':
  			xdigs = "0123456789abcdef";
 -hex:			_uintmax = UARG();
 +hex:
 +#ifdef POSITIONAL_PARAMS
 +			if (parg != NULL)
 +				_uintmax = get_arg_int(parg, argnum);
 +			else
 +#endif
 +			_uintmax = UARG(flags, ap);
  			base = HEX;
  			/* leading 0x/X only if non-zero */
  			if (flags & ALT && _uintmax != 0)
 @@ -797,6 +900,10 @@ done:
  error:
  	if (__sferror(fp))
  		ret = -1;
 +#ifdef POSITIONAL_PARAMS
 +	if (parg != NULL)
 +		free_parg(parg);
 +#endif
  	return (ret);
  }

 @@ -883,3 +990,213 @@ exponent(p0, expon, fmtch)
  	return (p - p0);
  }
  #endif /* FLOATING_POINT */
 +
 +#ifdef POSITIONAL_PARAMS
 +struct parg {
 +	int		flags;
 +	union	{
 +		uintmax_t	_uintmax;
 +		void		*_pointer;
 +		double		_double;
 +	} val;
 +};
 +
 +static void *parse_fmt(const char *);
 +
 +static uintmax_t
 +get_arg_int(void *parg, int argnum)
 +{
 +	struct parg *arg = parg;
 +
 +	if (argnum >= arg->flags)
 +		return 0;
 +	return arg[argnum].val._uintmax;
 +}
 +
 +static double
 +get_arg_double(void *parg, int argnum)
 +{
 +	struct parg *arg = parg;
 +
 +	if (argnum >= arg->flags)
 +		return 0;
 +	return arg[argnum].val._double;
 +}
 +
 +static void *
 +get_arg_ptr(void *parg, int argnum)
 +{
 +	struct parg *arg = parg;
 +
 +	if (argnum >= arg->flags)
 +		return 0;
 +	return arg[argnum].val._pointer;
 +}
 +
 +static void *
 +set_printf_args(const char *fmt, _BSD_VA_LIST_ ap)
 +{
 +	struct parg *arg;
 +	int flags;
 +	int i;
 +
 +	/*
 +	 * In order to set ap so that the n'th argument can be picked next
 +	 * we have to know the type of all the arguments.
 +	 */
 +	arg = parse_fmt(fmt);
 +	if (arg == NULL)
 +		return NULL;
 +
 +	/* Now walk ap along the arguments saving the values */
 +	for (i = 1; i < arg[0].flags; i++) {
 +		flags = arg[i].flags;
 +		if (flags == 0)
 +			/* unsigned int or arg not used in format */
 +			arg[i].val._uintmax = va_arg(ap, int);
 +		else if (flags & POINTER)
 +			if (flags & CHARINT)
 +				arg[i].val._pointer = va_arg(ap, char *);
 +			else
 +				arg[i].val._pointer = va_arg(ap, void *);
 +		else if (flags & FPT)
 +			if (flags & LONGDBL)
 +				arg[i].val._double = va_arg(ap, long double);
 +			else
 +				arg[i].val._double = va_arg(ap, double);
 +		else if (flags & SIGNED)
 +			arg[i].val._uintmax = SARG(flags, ap);
 +		else
 +			arg[i].val._uintmax = UARG(flags, ap);
 +	}
 +	return arg;
 +}
 +
 +static void
 +free_parg(void *arg)
 +{
 +
 +	free(arg);
 +}
 +
 +static void
 +save_arg(struct parg **parg, int argnum, int flags)
 +{
 +	struct parg *arg = *parg, *narg;
 +	int newlim;
 +
 +	if (argnum <= 0 || argnum > 1024)	/* Should be {NL_ARGMAX} */
 +		return;
 +
 +	if (arg == NULL || argnum >= arg[0].flags) {
 +		newlim = argnum + 15;
 +		narg = realloc(arg, newlim * sizeof *arg);
 +		if (narg == NULL)
 +			return;
 +		memset(narg + narg[0].flags, 0,
 +		    (newlim - narg[0].flags) * sizeof *arg);
 +		narg[0].flags = newlim;
 +		*parg = narg;
 +		arg = narg;
 +	}
 +	arg += argnum;
 +	arg->flags = flags;
 +}
 +
 +static void *
 +parse_fmt(const char *fmt)
 +{
 +	struct parg *arg = NULL;
 +	char *ncp;	/* avoid &fmt to kelp optimsation */
 +	int argnum;
 +	int flags;
 +	int n;
 +	char ch;
 +	wchar_t wc;
 +	mbstate_t ps;
 +
 +	mbrtowc(NULL, NULL, 0, &ps);
 +
 +	for (;;) {
 +		do {
 +			n = mbrtowc(&wc, fmt, MB_CUR_MAX, &ps);
 +			if (n <= 0)
 +				return arg;
 +			fmt += n;
 +		} while (wc != '%');
 +		flags = 0;
 +		argnum = 0;
 +		for (;;) {
 +			ch = *fmt++;
 +			switch (ch) {
 +			case ' ':	continue;
 +			case '#':	continue;
 +			case '-':	continue;
 +			case '+':	continue;
 +			case '*':	continue;
 +			case '0':	continue;
 +			case '.':	continue;
 +			case 'h':	flags |= SHORTINT;	continue;
 +			case 'j':	flags |= MAXINT;	continue;
 +#ifdef FLOATING_POINT
 +			case 'L':	flags |= LONGDBL;	continue;
 +#endif
 +			case 'q':	flags |= QUADINT;	continue;
 +			case 't':	flags |= PTRINT;	continue;
 +			case 'z':	flags |= SIZEINT;	continue;
 +			case 'l':
 +				if (fmt[0] == 'l') {
 +					fmt++;
 +					flags |= QUADINT;
 +				} else
 +					flags |= LONGINT;
 +				continue;
 +			case '1': case '2': case '3': case '4':
 +			case '5': case '6': case '7': case '8': case '9':
 +				n = strtoul(fmt - 1, &ncp, 10);
 +				fmt = ncp;
 +				if (*fmt == '$') {
 +					fmt++;
 +					if (argnum == 0)
 +						argnum = n;
 +					else
 +						save_arg(&arg, n, SIGNED);
 +				}
 +				continue;
 +			case 'D':
 +				flags |= LONGINT | SIGNED;
 +				break;
 +			case 'O': case 'U':
 +				flags |= LONGINT;
 +				break;
 +			case 'd': case 'i':
 +				flags |= SIGNED;
 +				break;
 +			case 'c': case 'o': case 'u': case 'X': case 'x':
 +				break;
 +#ifdef FLOATING_POINT
 +			case 'e': case 'f': case 'g':
 +			case 'E': case 'F': case 'G':
 +				flags |= FPT;
 +				break;
 +#endif /* FLOATING_POINT */
 +			case 's':
 +				flags |= CHARINT | POINTER;
 +				break;
 +			case 'n': case 'p':
 +				flags |= POINTER;
 +				break;
 +			default:
 +				/* Unexpected end of format */
 +				if (ch == 0)
 +					return arg;
 +				ch = 0;
 +				break;
 +			}
 +			if (ch != 0)
 +				save_arg(&arg, argnum, flags);
 +			break;
 +		}
 +	}
 +}
 +#endif

 -- 
 David Laight: david@l8s.co.uk

From: Charles Blundell <cb@netbsd.org>
To: David Laight <david@l8s.co.uk>
Cc: gnats-bugs@gnats.netbsd.org
Subject: Re: lib/1862: printf/scanf don't support X/Open positional parameters
Date: Mon, 1 Sep 2003 17:32:41 +0100

 on Mon, Sep 01, 2003 at 03:50:02PM +0100, David Laight wrote:
 > These overwrites can all be done with standard format strings, but the
 > %n$ variants make it so much easier!

 When the parameters to printf are bounded to some length, the
 presence of this feature can make things possible that were not
 previously.

 > Index: vfprintf.c
 > ===================================================================
 > RCS file: /cvsroot/src/lib/libc/stdio/vfprintf.c,v
 > retrieving revision 1.45
 > diff -u -p -r1.45 vfprintf.c
 > --- vfprintf.c	2003/08/07 16:43:34	1.45
 > +++ vfprintf.c	2003/09/01 14:43:02
 > +static uintmax_t
 > +get_arg_int(void *parg, int argnum)
 > +{
 > +	struct parg *arg = parg;
 > +
 > +	if (argnum >= arg->flags)
 > +		return 0;
 > +	return arg[argnum].val._uintmax;
 > +}

 I think you need to check for a negative argnum here, since argnum
 is the return from strtoul? (which allows it to vary above INT_MAX
 and negatives..)

 Likewise for get_arg_double and get_arg_ptr.


From: Simon Gerraty <sjg@juniper.net>
To: David Laight <david@l8s.co.uk>
Cc: gnats-bugs@gnats.netbsd.org
Subject: Re: lib/1862: printf/scanf don't support X/Open positional parameters 
Date: Mon, 01 Sep 2003 09:53:56 -0700

 On Mon, 1 Sep 2003 15:50:02 +0100, David Laight writes:
 >I've just sat down and implemented this for printf.

 Thanks.  Do we _need_ this though?

 >However there are definite security problems lurking for scanf and
 >even parts of printf.

 Yep, I recall a paper about just how easy it is to use this feature 
 to hack addresses in linux.

 What's the rationale (X/Open's) for these changes anyway?

 Thanks
 --sjg

From: Martin Husemann <martin@duskware.de>
To: Simon Gerraty <sjg@juniper.net>
Cc: David Laight <david@l8s.co.uk>, gnats-bugs@gnats.netbsd.org
Subject: Re: lib/1862: printf/scanf don't support X/Open positional parameters
Date: Mon, 1 Sep 2003 19:08:01 +0200

 On Mon, Sep 01, 2003 at 09:53:56AM -0700, Simon Gerraty wrote:
 > What's the rationale (X/Open's) for these changes anyway?

 You load the format string from a message catalog and translators are able to
 move filled in parameters around. Pretty nice feature, but the specification
 is way overcomplicated. It would be enough to allow positional pre-formated
 string parameters, maybe with a width field.

 (That's my understanding; I don't know anything of X/Open's motivation)

 Martin

From: David Laight <david@l8s.co.uk>
To: Martin Husemann <martin@duskware.de>
Cc: Simon Gerraty <sjg@juniper.net>, gnats-bugs@gnats.netbsd.org
Subject: Re: lib/1862: printf/scanf don't support X/Open positional parameters
Date: Mon, 1 Sep 2003 20:58:14 +0100

 On Mon, Sep 01, 2003 at 07:08:01PM +0200, Martin Husemann wrote:
 > On Mon, Sep 01, 2003 at 09:53:56AM -0700, Simon Gerraty wrote:
 > > What's the rationale (X/Open's) for these changes anyway?
 > 
 > You load the format string from a message catalog and translators are able to
 > move filled in parameters around. Pretty nice feature, but the specification
 > is way overcomplicated. It would be enough to allow positional pre-formated
 > string parameters, maybe with a width field.

 I think that part of the original rational came from an internationalisation
 process that extracted all (and I mean ALL) of the text strings from programs
 into a message catalog.  The translators where then expected to be able
 to translate the numbered string fragments without any context!
 (even "\n" got into the catalog!)

 The only problem I see is that "%*.*d" isn't treated as a single entity
 and always requires 3 argument specifiers "%3$*1$.*2$d".

 My 'gut' feel is that the original implementation (and hence the syntax)
 assumed that all the arguments (to printf) were the same size on the stack
 and hence that the stack could just be indexed to find the desired value.

 I think I might actually reduce the maximum number of selectable
 arguments to 9 (the minimum that IEEE 1003.1 2001 requires) in order to
 make it much harder to do a sucessful hack - and make the code simpler.

 > (That's my understanding; I don't know anything of X/Open's motivation)

 I've just looked at the X/Open XRAT, nothing about printf there [1].
 There is an implementation in the 1996 Unixware 2 sources, the copyright
 suggests this goes back to 1988 (and was always copyright Novell - IIRC
 they changed all the copyrights to be Novell regardless of the original one!)
 However this doesn't look like the original implementation (and I definitely
 haven't copied it!).  I don't have any pre-UW2 SVR4 sources.

 	David

 [1] some interesting words about mkdir xxx/ though

 -- 
 David Laight: david@l8s.co.uk

From: "Perry E. Metzger" <perry@piermont.com>
To: David Laight <david@l8s.co.uk>
Cc: gnats-bugs@gnats.netbsd.org
Subject: Re: lib/1862: printf/scanf don't support X/Open positional parameters
Date: 02 Sep 2003 16:05:03 -0400

 David Laight <david@l8s.co.uk> writes:
 > I've just sat down and implemented this for printf.

 One note. Open and FreeBSD have an implementation of this already. I
 think it would be best if we shared their implementation, if only so
 that we could avoid implementing new tweaks in the future by stealing
 their code...

 Perry

From: "Steven M. Bellovin" <smb@research.att.com>
To: "Perry E. Metzger" <perry@piermont.com>
Cc: David Laight <david@l8s.co.uk>, gnats-bugs@gnats.netbsd.org
Subject: Re: lib/1862: printf/scanf don't support X/Open positional parameters 
Date: Tue, 02 Sep 2003 16:32:55 -0400

 In message <871xuz553k.fsf@snark.piermont.com>, "Perry E. Metzger" writes:
 >
 >David Laight <david@l8s.co.uk> writes:
 >> I've just sat down and implemented this for printf.
 >
 >One note. Open and FreeBSD have an implementation of this already. I
 >think it would be best if we shared their implementation, if only so
 >that we could avoid implementing new tweaks in the future by stealing
 >their code...

 But perhaps we shouldn't use the feature in any NetBSD code...


 		--Steve Bellovin, http://www.research.att.com/~smb



From: "Perry E. Metzger" <perry@piermont.com>
To: "Steven M. Bellovin" <smb@research.att.com>
Cc: David Laight <david@l8s.co.uk>, gnats-bugs@gnats.netbsd.org
Subject: Re: lib/1862: printf/scanf don't support X/Open positional parameters
Date: 02 Sep 2003 16:39:15 -0400

 "Steven M. Bellovin" <smb@research.att.com> writes:
 > But perhaps we shouldn't use the feature in any NetBSD code...

 I tend to agree that the main purpose of implementing this should be
 standards compliance.

 -- 
 Perry E. Metzger		perry@piermont.com
From: David Holland <dholland-bugs@netbsd.org>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: lib/1862: printf/scanf don't support X/Open positional parameters
Date: Sat, 9 Feb 2008 18:28:03 +0000

 Updating this for the century of the fruitbat: positional parameter
 support has been committed for printf, but not scanf.

 -- 
 David A. Holland
 dholland@netbsd.org

State-Changed-From-To: open->suspended
State-Changed-By: dholland@NetBSD.org
State-Changed-When: Wed, 27 Aug 2014 19:26:27 +0000
State-Changed-Why:
The conclusion some time ago was that we didn't want positional parameters
for scanf due to it being an unnecessary attack/exploit vector.


State-Changed-From-To: suspended->closed
State-Changed-By: dholland@NetBSD.org
State-Changed-When: Sun, 26 Jun 2016 06:49:48 +0000
State-Changed-Why:
The conclusion some time ago was that we didn't want positional parameters
for scanf due to it being an unnecessary attack/exploit vector.

Thus: WONTFIX.
(the printf part got done a long time ago)


>Unformatted:

 See also standards/19556.

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.39 2013/11/01 18:47:49 spz Exp $
$NetBSD: gnats_config.sh,v 1.8 2006/05/07 09:23:38 tsutsui Exp $
Copyright © 1994-2007 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.