bin/122659: [patch] sh(1) long arithmetics broken on certain architectures

Jaakko Heinonen jh at saunalahti.fi
Fri Apr 11 14:50:01 UTC 2008


>Number:         122659
>Category:       bin
>Synopsis:       [patch] sh(1) long arithmetics broken on certain architectures
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Fri Apr 11 14:50:00 UTC 2008
>Closed-Date:
>Last-Modified:
>Originator:     Jaakko Heinonen
>Release:        FreeBSD 7.0-RELEASE amd64
>Organization:
>Environment:
System: FreeBSD x 7.0-RELEASE FreeBSD 7.0-RELEASE #0: Sun Feb 24 10:35:36 UTC 2008 root at driscoll.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC amd64

>Description:
This PR partially supersedes bin/51171.

In src/bin/sh/shell.h there's a following comment:

/*
 * Type of used arithmetics. SUSv3 requires us to have at least signed long.
 */

However in sh(1) code int type is used in several places for arithmetics
which prevents long arithmetics to work on architectures where
sizeof(int) != sizeof(long).

>How-To-Repeat:
$ sh -c 'uname -m; echo $((2147483647 + 1))'
amd64
-2147483648
>Fix:
I have attached two patches here. The first one converts places still using
int instead of arith_t to use arith_t. It also converts hardcoded format
strings to ARITH_FORMAT_STR and removes the hardcoded limitation of 10
characters for numbers.

The second patch which must be applied on top of the first one converts
arith_t from long to intmax_t. This patch is not strictly needed to comply
with SUS but it extends 32 bit arithmetics to 64 bits for example on i386.

--- sh-long-arith.diff begins here ---
Index: arith.h
===================================================================
RCS file: /home/ncvs/src/bin/sh/arith.h,v
retrieving revision 1.11
diff -p -u -r1.11 arith.h
--- arith.h	13 Aug 2005 07:59:46 -0000	1.11
+++ arith.h	9 Apr 2008 12:19:06 -0000
@@ -30,8 +30,10 @@
  * $FreeBSD: src/bin/sh/arith.h,v 1.11 2005/08/13 07:59:46 stefanf Exp $
  */
 
+#define lstrlen(var) (3 + (2 + CHAR_BIT * sizeof((var))) / 3)
+
 extern char *arith_buf, *arith_startbuf;
 
-int arith(char *);
+arith_t arith(char *);
 void arith_lex_reset(void);
 int expcmd(int, char **);
Index: arith.y
===================================================================
RCS file: /home/ncvs/src/bin/sh/arith.y,v
retrieving revision 1.21
diff -p -u -r1.21 arith.y
--- arith.y	13 Aug 2005 07:59:46 -0000	1.21
+++ arith.y	9 Apr 2008 12:19:06 -0000
@@ -43,8 +43,8 @@ __FBSDID("$FreeBSD: src/bin/sh/arith.y,v
 #include <limits.h>
 #include <stdio.h>
 
-#include "arith.h"
 #include "shell.h"
+#include "arith.h"
 #include "var.h"
 %}
 %union {
@@ -75,7 +75,10 @@ __FBSDID("$FreeBSD: src/bin/sh/arith.y,v
 
 exp:
 	expr
-		{ return ($1); }
+		{
+		*(YYPARSE_PARAM) = ($1);
+		return (0);
+		}
 	;
 
 expr:
@@ -259,12 +262,13 @@ expr:
 #include "output.h"
 #include "memalloc.h"
 
-#define lstrlen(var) (3 + (2 + CHAR_BIT * sizeof((var))) / 3)
+#define YYPARSE_PARAM_TYPE arith_t *
+#define YYPARSE_PARAM result
 
 char *arith_buf, *arith_startbuf;
 
 int yylex(void);
-int yyparse(void);
+int yyparse(YYPARSE_PARAM_TYPE);
 
 static int
 arith_assign(char *name, arith_t value)
@@ -279,15 +283,15 @@ arith_assign(char *name, arith_t value)
 	return ret;
 }
 
-int
+arith_t
 arith(char *s)
 {
-	long result;
+	arith_t result;
 
 	arith_buf = arith_startbuf = s;
 
 	INTOFF;
-	result = yyparse();
+	yyparse(&result);
 	arith_lex_reset();	/* Reprime lex. */
 	INTON;
 
@@ -313,7 +317,7 @@ expcmd(int argc, char **argv)
 	char *p;
 	char *concat;
 	char **ap;
-	long i;
+	arith_t i;
 
 	if (argc > 1) {
 		p = argv[1];
@@ -338,8 +342,8 @@ expcmd(int argc, char **argv)
 
 	i = arith(p);
 
-	out1fmt("%ld\n", i);
-	return !i;
+	out1fmt(ARITH_FORMAT_STR "\n", i);
+	return ((int)!i);
 }
 
 /*************************/
Index: arith_lex.l
===================================================================
RCS file: /home/ncvs/src/bin/sh/arith_lex.l,v
retrieving revision 1.24
diff -p -u -r1.24 arith_lex.l
--- arith_lex.l	13 Aug 2005 07:59:46 -0000	1.24
+++ arith_lex.l	9 Apr 2008 12:19:06 -0000
@@ -42,8 +42,8 @@ __FBSDID("$FreeBSD: src/bin/sh/arith_lex
 
 #include <string.h>
 
-#include "arith.h"
 #include "shell.h"
+#include "arith.h"
 #include "y.tab.h"
 #include "error.h"
 #include "memalloc.h"
Index: expand.c
===================================================================
RCS file: /home/ncvs/src/bin/sh/expand.c,v
retrieving revision 1.51
diff -p -u -r1.51 expand.c
--- expand.c	7 Nov 2006 22:46:13 -0000	1.51
+++ expand.c	9 Apr 2008 12:19:06 -0000
@@ -356,7 +356,7 @@ void
 expari(int flag)
 {
 	char *p, *start;
-	int result;
+	arith_t result;
 	int begoff;
 	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
 	int quoted;
@@ -372,10 +372,7 @@ expari(int flag)
 	 * have to rescan starting from the beginning since CTLESC
 	 * characters have to be processed left to right.
 	 */
-#if INT_MAX / 1000000000 >= 10 || INT_MIN / 1000000000 <= -10
-#error "integers with more than 10 digits are not supported"
-#endif
-	CHECKSTRSPACE(12 - 2, expdest);
+	CHECKSTRSPACE(lstrlen(result) - 2, expdest);
 	USTPUTC('\0', expdest);
 	start = stackblock();
 	p = expdest - 2;
@@ -397,7 +394,7 @@ expari(int flag)
 	if (quotes)
 		rmescapes(p+2);
 	result = arith(p+2);
-	fmtstr(p, 12, "%d", result);
+	fmtstr(p, lstrlen(result), ARITH_FORMAT_STR, result);
 	while (*p++)
 		;
 	if (quoted == 0)
--- sh-long-arith.diff ends here ---

--- sh-arith-intmax_t.diff begins here ---
--- sh/shell.h	2008-04-09 13:10:06.000000000 +0300
+++ sh/shell.h	2008-04-09 13:16:03.000000000 +0300
@@ -33,6 +33,8 @@
  * $FreeBSD: src/bin/sh/shell.h,v 1.17 2004/04/06 20:06:51 markm Exp $
  */
 
+#include <inttypes.h>
+
 /*
  * The follow should be set to reflect the type of system you have:
  *	JOBS -> 1 if you have Berkeley job control, 0 otherwise.
@@ -50,10 +52,10 @@
 /*
  * Type of used arithmetics. SUSv3 requires us to have at least signed long.
  */
-typedef long arith_t;
-#define	ARITH_FORMAT_STR  "%ld"
-#define	atoarith_t(arg)  strtol(arg, NULL, 0)
-#define	strtoarith_t(nptr, endptr, base)  strtol(nptr, endptr, base)
+typedef intmax_t arith_t;
+#define	ARITH_FORMAT_STR  "%" PRIdMAX
+#define	atoarith_t(arg)  strtoimax(arg, NULL, 0)
+#define	strtoarith_t(nptr, endptr, base)  strtoimax(nptr, endptr, base)
 
 typedef void *pointer;
 #define STATIC  static
--- sh-arith-intmax_t.diff ends here ---


>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list