php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #76107 SplFileInfo returns incorrect information on large file in 32bit architecture
Submitted: 2018-03-17 06:45 UTC Modified: 2020-04-16 14:34 UTC
Votes:1
Avg. Score:5.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:0 (0.0%)
Same OS:0 (0.0%)
From: VeryCrazyDog at gmail dot com Assigned:
Status: Open Package: SPL related
PHP Version: 7.2.3 OS: Arch Linux ARMv7
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: VeryCrazyDog at gmail dot com
New email:
PHP Version: OS:

 

 [2018-03-17 06:45 UTC] VeryCrazyDog at gmail dot com
Description:
------------
When calling SplFileInfo::isFile() on a large file, false is returned instead of true.

When calling SplFileInfo::getMTime() and SplFileInfo::getSize() on a large file, RuntimeException is throw.

This problem does not happens on Windows 7 64bit running same PHP version. It seems that it is related the maximum value of int, because the file size which I used in the test is "2939977728" which cannot be fit in 32bit OS.

Although it may not be possible to report the file size, it is expected at least, isFile() and getMTime() should return the correct result.

Test script:
---------------
<?php
$info = new SplFileInfo('/path/to/large_file');
var_dump($info);
var_dump($info->isFile());  // This returns false
var_dump($info->isReadable());
var_dump($info->getMTime());  // This throws RuntimeException
var_dump($info->getSize());  // This throws RuntimeException
if ($info->isFile()) {
	echo $info->getRealPath();
}


Expected result:
----------------
vcd@server ~$ php -f test.php
object(SplFileInfo)#1 (2) {
  ["pathName":"SplFileInfo":private]=>
  string(24) "/home/vcd/tmp/large_file"
  ["fileName":"SplFileInfo":private]=>
  string(10) "large_file"
}
bool(true)
bool(true)
int(1515414305)
bool(false)
/home/vcd/tmp/large_file


Actual result:
--------------
vcd@server ~$ php -f test.php
object(SplFileInfo)#1 (2) {
  ["pathName":"SplFileInfo":private]=>
  string(24) "/home/vcd/tmp/large_file"
  ["fileName":"SplFileInfo":private]=>
  string(10) "large_file"
}
bool(false)
bool(true)
PHP Fatal error:  Uncaught RuntimeException: SplFileInfo::getMTime(): stat failed for /home/vcd/tmp/large_file in /home/vcd/test.php:6
Stack trace:
#0 /home/vcd/test.php(6): SplFileInfo->getMTime()
#1 {main}
  thrown in /home/vcd/test.php on line 6


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2020-04-15 17:00 UTC] cmb@php.net
Presumably, stat() fails with EOVERFLOW here, what could at least
trigger a more descriptive error message.

Interestingly, the given script mostly works on Windows x86,
albeit the reported size is wrong due to overflow[1].

[1] <https://github.com/php/php-src/blob/php-7.3.17/Zend/zend_virtual_cwd.c#L352-L355>
 [2020-04-16 12:29 UTC] VeryCrazyDog at gmail dot com
I tried to run the following C program, but no error occurred, maybe the error is throw somewhere else?

---------------
#include <stdio.h>
#include <stdint.h>

typedef int32_t zend_long;

int main (void) {
    zend_long l = 2147483647;
    // Print: long: 2147483647
    printf("long: %d\n", l);
    l++;
    // Print: long: -2147483648
    printf("long: %d\n", l);

    long long int ll = 2721514410;
    // Print: long long: 2721514410
    printf("long long: %lld\n", ll);
    l = (zend_long)ll;
    // Print: long: -1573452886
    printf("long: %d\n", l);
    return 0;
}
---------------

I am now running PHP 7.4.3 and the same result and same error occurred. Besides a more descriptive error message, is that possible to give correct output when isFile() and getMTime() are called?
 [2020-04-16 13:55 UTC] cmb@php.net
You can try something like the following (untested):


#include <stdio.h>
#include <sys/stat.h>

int main()
{
    struct stat ssb;
    int res = stat("/path/to/large/file", &ssb);
    printf("result: %d, errno: %d, EOVERFLOW: %d", res, errno, EOVERFLOW);
    return 0;
}


I assume that will show that errno is EOVERFLOW.

Anyhow, I don't think we can really fix this, since the stat()
call is important, and if it fails there's not much we can do,
besides giving a better error message.
 [2020-04-16 14:34 UTC] VeryCrazyDog at gmail dot com
I tested using your source code, indeed yes, EOVERFLOW returned without result. Agreed that nothing can be done here. Shall I close this bug or shall I leave it open for improving the error message?

A side question, since "stat" Linux command works, there should have a way to get the file info (such as modified date), but I can imagine the change will be huge which may not be justify to do, right?


Output:
------------
result: -1, errno: 75, EOVERFLOW: 75

File type:                regular file
I-node number:            1697634
Mode:                     100644 (octal)
Link count:               1
Ownership:                UID=1000   GID=100
Preferred I/O block size: 0 bytes
File size:                -1573452886 bytes
Blocks allocated:         0
Last status change:       Thu Jan  1 08:00:00 1970
Last file access:         Thu Jan  1 08:00:00 1970
Last file modification:   Thu Jan  1 08:00:00 1970
------------



Source code:
------------
#include <stdio.h>
#include <sys/stat.h>
#include <errno.h>
#include <time.h>

int main()
{
	struct stat sb = {0};
	int res = stat("/home/vcd/tmp/LARGE_FILE", &sb);
	printf("result: %d, errno: %d, EOVERFLOW: %d\n\n", res, errno, EOVERFLOW);

	printf("File type:                ");
	switch (sb.st_mode & S_IFMT) {
		case S_IFBLK:  printf("block device\n");            break;
		case S_IFCHR:  printf("character device\n");        break;
		case S_IFDIR:  printf("directory\n");               break;
		case S_IFIFO:  printf("FIFO/pipe\n");               break;
		case S_IFLNK:  printf("symlink\n");                 break;
		case S_IFREG:  printf("regular file\n");            break;
		case S_IFSOCK: printf("socket\n");                  break;
		default:       printf("unknown?\n");                break;
	}
	printf("I-node number:            %ld\n", (long) sb.st_ino);
	printf("Mode:                     %lo (octal)\n", (unsigned long) sb.st_mode);
	printf("Link count:               %ld\n", (long) sb.st_nlink);
	printf("Ownership:                UID=%ld   GID=%ld\n", (long) sb.st_uid, (long) sb.st_gid);
	printf("Preferred I/O block size: %ld bytes\n", (long) sb.st_blksize);
	printf("File size:                %lld bytes\n", (long long) sb.st_size);
	printf("Blocks allocated:         %lld\n", (long long) sb.st_blocks);
	printf("Last status change:       %s", ctime(&sb.st_ctime));
	printf("Last file access:         %s", ctime(&sb.st_atime));
	printf("Last file modification:   %s", ctime(&sb.st_mtime));
	return 0;
}
------------
 
PHP Copyright © 2001-2021 The PHP Group
All rights reserved.
Last updated: Wed Jul 28 04:01:25 2021 UTC