Skip to content

Commit

Permalink
detect struct stat.st_dev's size and signedness, and return it safely
Browse files Browse the repository at this point in the history
On FreeBSD dev_t (and hence the st_dev member of struct stat) is an
unsigned 64-bit integer, and the previous simple PUSHi() corrupted
that.

A previous version of this reflected the st_ino code and implemented
our own number to string conversion, but a system with such a large
st_dev should be assumed to have inttypes.h, and an intmax_t which is
no smaller than st_dev.

The st_ino code could probably be changed similarly, but 64-bit inode
numbers are not a new thing, so it may be riskier.
  • Loading branch information
tonycoz committed Sep 1, 2021
1 parent b852e1d commit 9b56997
Show file tree
Hide file tree
Showing 15 changed files with 162 additions and 3 deletions.
72 changes: 72 additions & 0 deletions Configure
Expand Up @@ -1321,6 +1321,8 @@ shsharp=''
spitshell=''
src=''
ssizetype=''
st_dev_sign=''
st_dev_size=''
st_ino_sign=''
st_ino_size=''
startperl=''
Expand Down Expand Up @@ -22744,6 +22746,74 @@ else
fi
$rm_try

: Check the size of st_dev
$echo " "
$echo "Checking the size of st_dev..." >&4
$cat > try.c <<EOCP
#include <sys/stat.h>
#include <stdio.h>
#$i_stdlib I_STDLIB
#ifdef I_STDLIB
#include <stdlib.h>
#endif
int main() {
struct stat st;
printf("%d\n", (int)sizeof(st.st_dev));
exit(0);
}
EOCP
set try
if eval $compile_ok; then
val=`$run ./try`
case "$val" in
'') st_dev_size=4
$echo "(I can't execute the test program--guessing $st_dev_size.)" >&4
;;
*) st_dev_size=$val
$echo "Your st_dev is $st_dev_size bytes long."
;;
esac
else
st_dev_size=4
$echo "(I can't compile the test program--guessing $st_dev_size.)" >&4
fi
$rm_try

: Check if st_dev is signed
$echo " "
$echo "Checking the sign of st_dev..." >&4
$cat > try.c <<EOCP
#include <sys/stat.h>
#include <stdio.h>
int main() {
struct stat foo;
foo.st_dev = -1;
if (foo.st_dev < 0)
printf("-1\n");
else
printf("1\n");
}
EOCP
set try
if eval $compile; then
val=`$run ./try`
case "$val" in
'') st_dev_sign=1
$echo "(I can't execute the test program--guessing unsigned.)" >&4
;;
*) st_dev_sign=$val
case "$st_dev_sign" in
1) $echo "Your st_dev is unsigned." ;;
-1) $echo "Your st_dev is signed." ;;
esac
;;
esac
else
st_dev_sign=1
$echo "(I can't compile the test program--guessing unsigned.)" >&4
fi
$rm_try

: see what type of char stdio uses.
echo " "
echo '#include <stdio.h>' | $cppstdin $cppminus > stdioh
Expand Down Expand Up @@ -25299,6 +25369,8 @@ srand48_r_proto='$srand48_r_proto'
srandom_r_proto='$srandom_r_proto'
src='$src'
ssizetype='$ssizetype'
st_dev_sign='$st_dev_sign'
st_dev_size='$st_dev_size'
st_ino_sign='$st_ino_sign'
st_ino_size='$st_ino_size'
startperl='$startperl'
Expand Down
2 changes: 2 additions & 0 deletions Cross/config.sh-arm-linux
Expand Up @@ -1078,6 +1078,8 @@ srand48_r_proto='0'
srandom_r_proto='0'
src='.'
ssizetype='ssize_t'
st_dev_sign='1'
st_dev_size='4'
st_ino_sign='1'
st_ino_size='4'
startperl='#!/usr/bin/perl'
Expand Down
2 changes: 2 additions & 0 deletions Cross/config.sh-arm-linux-n770
Expand Up @@ -1076,6 +1076,8 @@ srand48_r_proto='0'
srandom_r_proto='0'
src='.'
ssizetype='ssize_t'
st_dev_sign='1'
st_dev_size='4'
st_ino_sign='1'
st_ino_size='4'
startperl='#!/usr/bin/perl'
Expand Down
2 changes: 2 additions & 0 deletions NetWare/config.wc
Expand Up @@ -1042,6 +1042,8 @@ srand48_r_proto='0'
srandom_r_proto='0'
src=''
ssizetype='int'
st_dev_sign='1'
st_dev_size='4'
st_ino_sign='1'
st_ino_size='4'
startperl='#!perl'
Expand Down
2 changes: 2 additions & 0 deletions Porting/config.sh
Expand Up @@ -1107,6 +1107,8 @@ srand48_r_proto='REENTRANT_PROTO_I_LS'
srandom_r_proto='REENTRANT_PROTO_I_TS'
src='.'
ssizetype='ssize_t'
st_dev_sign='1'
st_dev_size='4'
st_ino_sign='1'
st_ino_size='8'
startperl='#!/opt/perl/bin/perl5.35.4'
Expand Down
9 changes: 9 additions & 0 deletions config_h.SH
Expand Up @@ -4198,13 +4198,22 @@ sed <<!GROK!THIS! >$CONFIG_H -e 's!^#undef\(.*/\)\*!/\*#define\1 \*!' -e 's!^#un
*/
#define SELECT_MIN_BITS $selectminbits /**/
/* ST_DEV_SIZE:
* This variable contains the size of struct stat's st_dev in bytes.
*/
/* ST_DEV_SIGN:
* This symbol holds the signedness of struct stat's st_dev.
* 1 for unsigned, -1 for signed.
*/
/* ST_INO_SIZE:
* This variable contains the size of struct stat's st_ino in bytes.
*/
/* ST_INO_SIGN:
* This symbol holds the signedness of struct stat's st_ino.
* 1 for unsigned, -1 for signed.
*/
#define ST_DEV_SIGN $st_dev_sign /* st_dev sign */
#define ST_DEV_SIZE $st_dev_size /* st_dev size */
#define ST_INO_SIGN $st_ino_sign /* st_ino sign */
#define ST_INO_SIZE $st_ino_size /* st_ino size */
Expand Down
2 changes: 2 additions & 0 deletions configure.com
Expand Up @@ -6971,6 +6971,8 @@ $ WC "src='" + src + "'"
$ WC "ssizetype='int'"
$ WC "startperl=" + startperl ! This one's special--no enclosing single quotes
$ WC "static_ext='" + static_ext + "'"
$ WC "st_dev_size='"4"'"
$ WC "st_dev_sign='1'"
$ WC "st_ino_size='" + st_ino_size + "'"
$ WC "st_ino_sign='1'"
$ WC "stdchar='" + stdchar + "'"
Expand Down
2 changes: 2 additions & 0 deletions plan9/config_sh.sample
Expand Up @@ -1049,6 +1049,8 @@ srand48_r_proto='0'
srandom_r_proto='0'
src='.'
ssizetype='ssize_t'
st_dev_sign='1'
st_dev_size='4'
st_ino_sign='1'
st_ino_size='4'
startperl='#!/bin/perl'
Expand Down
31 changes: 31 additions & 0 deletions pp_sys.c
Expand Up @@ -2916,7 +2916,38 @@ PP(pp_stat)
if (max) {
EXTEND(SP, max);
EXTEND_MORTAL(max);
#if ST_DEV_SIZE < IVSIZE || (ST_DEV_SIZE == IVSIZE && ST_DEV_SIGN < 0)
mPUSHi(PL_statcache.st_dev);
#elif ST_DEV_SIZE == IVSIZE
mPUSHu(PL_statcache.st_dev);
#else
# if ST_DEV_SIGN < 0
if (LIKELY((IV)PL_statcache.st_dev == PL_statcache.st_dev)) {
mPUSHi((IV)PL_statcache.st_dev);
}
# else
if (LIKELY((UV)PL_statcache.st_dev == PL_statcache.st_dev)) {
mPUSHu((UV)PL_statcache.st_dev);
}
# endif
else {
char buf[sizeof(PL_statcache.st_dev)*3+1];
/* sv_catpvf() casts 'j' size values down to IV, so it
isn't suitable for use here.
*/
# if defined(I_INTTYPES) && defined(HAS_SNPRINTF)
# if ST_DEV_SIGN < 0
int size = snprintf(buf, sizeof(buf), "%" PRIdMAX, (intmax_t)PL_statcache.st_dev);
# else
int size = snprintf(buf, sizeof(buf), "%" PRIuMAX, (uintmax_t)PL_statcache.st_dev);
# endif
STATIC_ASSERT_STMT(sizeof(intmax_t) >= sizeof(PL_statcache.st_dev));
mPUSHp(buf, size);
# else
# error extraordinarily large st_dev but no inttypes.h or no snprintf
# endif
}
#endif
{
/*
* We try to represent st_ino as a native IV or UV where
Expand Down
20 changes: 19 additions & 1 deletion t/op/stat.t
Expand Up @@ -29,7 +29,7 @@ if ($^O eq 'MSWin32') {

my $Errno_loaded = eval { require Errno };

plan tests => 110;
plan tests => 111;

my $Perl = which_perl();

Expand Down Expand Up @@ -664,6 +664,24 @@ SKIP: {
unlink $link;
}

SKIP:
{
# test needs a FreeBSD /usr/bin/stat
# /tmp is typically tmpfs on a new FreeBSD
$^O eq "freebsd"
or skip "only checking freebsd for now", 1;
-x "/usr/bin/stat"
or skip "no /usr/bin/stat", 1;
my @s = stat "/tmp";
@s or skip "No /tmp found", 1;
my $test = `/usr/bin/stat -f '%d %i' /tmp`;
$test && $test =~ /^-?\d+ -?\d+/
or skip "stat didn't return an expected result";
chomp $test;
is("$s[0] $s[1]", $test,
"perl stat didn't match system stat utility");
}

END {
chmod 0666, $tmpfile;
unlink_all $tmpfile;
Expand Down
13 changes: 11 additions & 2 deletions uconfig.h
Expand Up @@ -4163,13 +4163,22 @@
*/
#define SELECT_MIN_BITS 32 /**/

/* ST_DEV_SIZE:
* This variable contains the size of struct stat's st_dev in bytes.
*/
/* ST_DEV_SIGN:
* This symbol holds the signedness of struct stat's st_dev.
* 1 for unsigned, -1 for signed.
*/
/* ST_INO_SIZE:
* This variable contains the size of struct stat's st_ino in bytes.
*/
/* ST_INO_SIGN:
* This symbol holds the signedness of struct stat's st_ino.
* 1 for unsigned, -1 for signed.
*/
#define ST_DEV_SIGN 1 /* st_dev sign */
#define ST_DEV_SIZE 4 /* st_dev size */
#define ST_INO_SIGN 1 /* st_ino sign */
#define ST_INO_SIZE 4 /* st_ino size */

Expand Down Expand Up @@ -5318,6 +5327,6 @@
#endif

/* Generated from:
* f88762343858d290ac1b2bce8613d97ce15c18eada7a5a587fb9b8a8053d0d7f config_h.SH
* 24570f2ff38b22b436d34127ff85a3f3a075a4dea1359c56f16196bdbe376b1c uconfig.sh
* 4769474cd122c46e0efc0224475eb6401d7837cf93c9484af644422d1c5a68e3 config_h.SH
* 9aacfe7bf1c55c60b2116e885bafc3f0852a9f52f1ba926d598dd6aa271aa8a2 uconfig.sh
* ex: set ro: */
2 changes: 2 additions & 0 deletions uconfig.sh
Expand Up @@ -850,6 +850,8 @@ srand48_r_proto='0'
srandom_r_proto='0'
src='.'
ssizetype=int
st_dev_sign='1'
st_dev_size='4'
st_ino_sign='1'
st_ino_size='4'
startperl='#!perl'
Expand Down
2 changes: 2 additions & 0 deletions uconfig64.sh
Expand Up @@ -850,6 +850,8 @@ srand48_r_proto='0'
srandom_r_proto='0'
src='.'
ssizetype=long
st_dev_sign='1'
st_dev_size='4'
st_ino_sign='1'
st_ino_size='8'
startperl='#!perl'
Expand Down
2 changes: 2 additions & 0 deletions win32/config.gc
Expand Up @@ -1073,6 +1073,8 @@ srand48_r_proto='0'
srandom_r_proto='0'
src=''
ssizetype='int'
st_dev_sign='1'
st_dev_size='4'
st_ino_sign='1'
st_ino_size='4'
startperl='#!perl'
Expand Down
2 changes: 2 additions & 0 deletions win32/config.vc
Expand Up @@ -1072,6 +1072,8 @@ srand48_r_proto='0'
srandom_r_proto='0'
src=''
ssizetype='int'
st_dev_sign='1'
st_dev_size='4'
st_ino_sign='1'
st_ino_size='4'
startperl='#!perl'
Expand Down

0 comments on commit 9b56997

Please sign in to comment.