Bug 5079

Summary: Possible file date underflow on ext2/3 filesystems on amd64 systems
Product: File System Reporter: Carlos Silva (r3pek)
Component: ext3Assignee: Markus Rechberger (Markus.Rechberger)
Severity: normal CC: akpm, kernel, markus.mottl, zwane
Priority: P2    
Hardware: i386   
OS: Linux   
Kernel Version: 2.6.12-gentoo-r6 Subsystem:
Regression: --- Bisected commit-id:
Attachments: signed/unsigned inode fix

Description Carlos Silva 2005-08-17 04:26:12 UTC
Distribution: Gentoo
Hardware Environment: 64 bit machine
Problem Description:
A user at gentoo buzilla(1) reported that some files that he change the date to
1 Jan 1970 for archiving purposes, after umount and re-mounting the partition,
the files would have the date changed to 7 Feb 2106. On the report, he only test
this on ext3 filesystem but i also reproduce it in ext2 filesystems. If any mor
info is needed just ask.

Steps to reproduce:
1. Compile and install a kernel with ext2/3 support
2. mount <ext2/3_partition>
3. cd <ext2/3_partition>
4. touch -d 01/01/1907 test_file
5. cd ..
6. umount <ext2/3_partition>
7. mount <ext2/3_partition>
8. ls -l <ext2/3_partition>/test_file

The file will report 7 Feb 2106.

(1) http://bugs.gentoo.org/show_bug.cgi?id=101723
Comment 1 Carlos Silva 2005-08-23 08:01:59 UTC
This was also confirmed in plain vanilla kernel 2.6.13-rc6.
Comment 2 Carlos Silva 2005-08-23 08:18:05 UTC
btw, forgot to mention that this was only reproduced on 64bit machines (intel
and amd) and that this bug doesn't happen on reiserfs nor xfs, just ext2/3. Also
this is not new to the 2.6.13 kernel since it is reproducible on 2.6.12 kernel's.
Comment 3 Daniel Drake 2005-09-17 10:06:05 UTC
Downstream bug report: http://bugs.gentoo.org/show_bug.cgi?id=101723
Comment 4 Andrew Morton 2006-01-20 16:00:48 UTC
I cannot reproduce this with 2.6.16-rc1. I tried setting localtime to Japan too.
Can you guys please retest?

If it still happens, then we need to work out what's different between your
setups and mine.

Also, use /usr/bin/stat to read the inode timestamps - that'll help us separate
kernel problems from userspace ones.
Comment 5 Daniel Drake 2006-01-20 17:02:57 UTC
I am now on x86_64 and interestingly enough I was looking into this just a
little earlier.

I can reproduce the bug on my x86_64 but *not* on my x86. Note that the
partition must be remounted before the "future" date appears.

I didn't get much time to work on this, but I ended up confusing myself how it
can possibly work on x86. In ext2_update_inode(), the inode->i_mtime.tv_sec
value seems to be represented as 64 bits, but the raw_inode->i_mtime as 32.

The inode value was reading something like 0xffffff831212 (made up number, it
was similar to that though: first 32 bits as ff), and the raw_inode value was
being truncated to 0x831212.

You can probably discount these results as they were quite rushed. However I can
definately confirm the bug, and I'll come up with a detailed description of the
problem (and maybe even fix it :) when I next have some time (maybe this weekend).
Comment 6 Daniel Drake 2006-01-20 17:05:30 UTC
Found the numbers I was testing with in my logs.

When inode->i_mtime.tv_sec read 0xffffffff89808d00, 0x89808d00 was being written
into raw_inode->i_mtime.
Comment 7 Daniel Drake 2006-01-20 17:12:37 UTC
Ok I just gave it some thought and the bug is obvious. The function I've been
talking about is  ext2_update_inode() by the way, which deals with copying
values from an inode struct (inode) to an ext2_inode struct (raw_inode).

inode->i_mtime.tv_sec is a time_t which is a "long" for both x86 and x86_64. On
x86 this is 32 bits and on x86_64 it is 64 bits.

raw_inode->i_mtime is __le32.

This explains why it works on x86: cpu_to_le32() on a 32 bit value isn't gonna
hurt it

However, cpu_to_le32() on a 64 bit signed number (apparently) doesn't consider
its numerical value (in this case, negative, since we are talking about dates
before the epoch), it just chops off the upper bits which it doesn't care about.
Comment 8 Andrew Morton 2006-01-20 18:22:51 UTC
Yes, something like that.  Or the bug's in ext2_read_inode():
sign-extending a 32-bit field to 64.

But how are you setting an inode's time to before-epoch anyway?
If I try that, /bin/date complains.

Comment 9 Daniel Drake 2006-01-21 03:04:32 UTC
Using touch:

touch -d 01/01/1907 somefile
Comment 10 Andrew Morton 2006-01-21 03:23:58 UTC
oh, I thought 1907 was a typo in the bug report.

ext2 and ext3 cannot represent dates prior to epoch, and FC1's `touch'
command refuses to let you try.

But a newer version of `date' _does_ permit it.  As one would expect, a
negative time like this will turn into a large positive time after it's
been written to a 32-bit inode field and read back.

I guess the filesystem should be returning error on this, but it cannot
distinguish a negative time from a large positive one.

That being said, 2106 is the wrong year.  Setting the time to epoch-63
years _should_ result in epoch+73, but that's 2043.

I think the 2106 is epoch+0xffffffff, which is odd.

Comment 11 Daniel Drake 2006-01-21 03:47:07 UTC
Oh, it probably was a typo, as the description talks about 1 Jan 1970. I'll
retest for 1970 later, since that shouldn't suffer from this weird pre-epoch
crazyness, right?
Comment 12 Andreas Dilger 2006-02-03 17:08:10 UTC
With the advent of large inodes, we gain the ability to have more space for the
inode timestamps.  This has repeatedly been considered only for adding
nanosecond timestamps (consuming 30 bits per), but little consideration has been
given to also increasing the maximum values using some of those bits.

I also thought that unix timestamps are considered signed values, which is why
there is a unix "y2k" problem in 2038 (68 years = 2^31 seconds), so in theory
negative timestamps should get us to 1902 or so.  Whether we care about the
signedness for ext3 files is a completely different issue.
Comment 13 Hil Liao 2006-08-31 13:37:47 UTC
Has anyone looked into this problem? I am interested in the timestamp bug. Where
in the kernel source should I look into to fix the bug? Are there documents
about ext3 filesystem for developers?
Comment 14 Markus Rechberger 2007-02-06 08:37:23 UTC
Created attachment 10317 [details]
signed/unsigned inode fix
Comment 15 Markus Rechberger 2007-02-06 08:41:20 UTC
The previous attachment is an approach to fix the date problem.

I tested this fix on an amd64 machine:

unsigned long ranges from -2.147.483.648 

10000011110110100100111110111101 .. -2.082.844.739
10000011110110100100111110111101 .. <- this would be stored in 
long(64bit) which overflows the 32bit range/date

Comment 16 Markus Rechberger 2007-02-06 08:50:34 UTC
signed long ranges from -2.147.483.648 to 2.147.483.647 on x86 32bit

10000011110110100100111110111101 .. -2.082.844.739
10000011110110100100111110111101 .. <- this currently gets 
stored on the disk but when converting it to a 64bit signed long value it loses 
its sign and becomes positive.
Comment 17 Andrew Morton 2007-02-06 16:09:31 UTC
merged Markus's fix into -mm.
Comment 18 Dmitry Monakhov 2007-06-17 02:43:36 UTC
Btw: after time val becomes interpreted as signed value we have to make
according changes in e2fsprogs.
Comment 19 Andrew Morton 2007-06-17 03:01:12 UTC
*** Bug 8643 has been marked as a duplicate of this bug. ***
Comment 20 Theodore Tso 2009-01-17 19:44:55 UTC
Committed into mainline as commit: 4d7bf11d649c72621ca31b8ea12b9c94af380e63