Subject: bin/13943: /bin/sh: Arithmetic Expansion possibly overflow (a hotbed of Sept. 9 date problem)
To: None <gnats-bugs@gnats.netbsd.org>
From: None <h-suzuki@iij.ad.jp>
List: netbsd-bugs
Date: 09/13/2001 17:57:00
>Number:         13943
>Category:       bin
>Synopsis:       /bin/sh: Arithmetic Expansion possibly overflow (a hotbed of Sept. 9 date problem)
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    bin-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Thu Sep 13 01:58:01 PDT 2001
>Closed-Date:
>Last-Modified:
>Originator:     SUZUKI Hideaki
>Release:        NetBSD 1.5.1
>Organization:
	
>Environment:
	
System: NetBSD lepracaun 1.5.1 NetBSD 1.5.1 (LEPRACAUN) #2: Fri Jul 13 22:05:16 JST 2001 root@lepracaun.iij.ad.jp:/usr/src/sys/arch/i386/compile/LEPRACAUN i386

>Description:

The following sample script executes successfully when $today has a
value under 1,000,000,000. However, if $today has 1,000,000,000 or
more, the script returns incorrect result.

sample script:

----- cut here -----  
#!/bin/sh

today=$(date +"%s")
echo $today

# if use $((expression))
yesterday=$((${today} - 86400))
echo $yesterday

# if use $(expr expression)
yesterdayoncemore=$(expr ${today} - 86400)
echo $yesterdayoncemore
# eof
----- cut here -----

The reason is in NetBSD/src/bin/sh/expand.c:expari():

   int result;

   (snip)

   result = arith(p+2);
   fmtstr(p, 10, "%d", result);

I believe fmtstr() function must not round off 'result' on 10 digits.
because 'int' valiable ranges from -2,147,483,648 to 2,147,483,647.


>How-To-Repeat:

Run something like as follows:

----- cut here -----
#!/bin/sh

today=$(date +"%s")
today2=$(date -r $today)
echo 'today:'
echo 'unixtime =' $today
echo $today2
echo 

# if use $((expression))
#
yesterday=$((${today} - 86400))
yesterday2=$(date -r $yesterday)
echo 'yesterday:'
echo 'unixtime =' $yesterday
echo $yesterday2
echo

# if use $(expr expression)
#
yesterdayoncemore=$(expr ${today} - 86400)
yesterdayoncemore2=$(date -r $yesterdayoncemore)
echo 'yesterdayoncemore:'
echo 'unixtime =' $yesterdayoncemore
echo $yesterdayoncemore2
# eof
----- cut here -----

>Fix:

*** /usr/src/bin/sh/expand.c.orig	Wed Sep 12 19:38:03 2001
--- /usr/src/bin/sh/expand.c	Wed Sep 12 19:38:25 2001
***************
*** 393,399 ****
  	if (quotes)
  		rmescapes(p+2);
  	result = arith(p+2);
! 	fmtstr(p, 10, "%d", result);
  
  	while (*p++)
  		;
--- 393,399 ----
  	if (quotes)
  		rmescapes(p+2);
  	result = arith(p+2);
! 	fmtstr(p, 12, "%d", result);
  
  	while (*p++)
  		;
>Release-Note:
>Audit-Trail:
>Unformatted: