Skip to content

Commit b586c16

Browse files
committed
detect struct stat.st_dev's size and signedness, and return it safely
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.
1 parent 6577de2 commit b586c16

File tree

15 files changed

+162
-3
lines changed

15 files changed

+162
-3
lines changed

Configure

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1320,6 +1320,8 @@ shsharp=''
13201320
spitshell=''
13211321
src=''
13221322
ssizetype=''
1323+
st_dev_sign=''
1324+
st_dev_size=''
13231325
st_ino_sign=''
13241326
st_ino_size=''
13251327
startperl=''
@@ -22695,6 +22697,74 @@ else
2269522697
fi
2269622698
$rm_try
2269722699

22700+
: Check the size of st_dev
22701+
$echo " "
22702+
$echo "Checking the size of st_dev..." >&4
22703+
$cat > try.c <<EOCP
22704+
#include <sys/stat.h>
22705+
#include <stdio.h>
22706+
#$i_stdlib I_STDLIB
22707+
#ifdef I_STDLIB
22708+
#include <stdlib.h>
22709+
#endif
22710+
int main() {
22711+
struct stat st;
22712+
printf("%d\n", (int)sizeof(st.st_dev));
22713+
exit(0);
22714+
}
22715+
EOCP
22716+
set try
22717+
if eval $compile_ok; then
22718+
val=`$run ./try`
22719+
case "$val" in
22720+
'') st_dev_size=4
22721+
$echo "(I can't execute the test program--guessing $st_dev_size.)" >&4
22722+
;;
22723+
*) st_dev_size=$val
22724+
$echo "Your st_dev is $st_dev_size bytes long."
22725+
;;
22726+
esac
22727+
else
22728+
st_dev_size=4
22729+
$echo "(I can't compile the test program--guessing $st_dev_size.)" >&4
22730+
fi
22731+
$rm_try
22732+
22733+
: Check if st_dev is signed
22734+
$echo " "
22735+
$echo "Checking the sign of st_dev..." >&4
22736+
$cat > try.c <<EOCP
22737+
#include <sys/stat.h>
22738+
#include <stdio.h>
22739+
int main() {
22740+
struct stat foo;
22741+
foo.st_dev = -1;
22742+
if (foo.st_dev < 0)
22743+
printf("-1\n");
22744+
else
22745+
printf("1\n");
22746+
}
22747+
EOCP
22748+
set try
22749+
if eval $compile; then
22750+
val=`$run ./try`
22751+
case "$val" in
22752+
'') st_dev_sign=1
22753+
$echo "(I can't execute the test program--guessing unsigned.)" >&4
22754+
;;
22755+
*) st_dev_sign=$val
22756+
case "$st_dev_sign" in
22757+
1) $echo "Your st_dev is unsigned." ;;
22758+
-1) $echo "Your st_dev is signed." ;;
22759+
esac
22760+
;;
22761+
esac
22762+
else
22763+
st_dev_sign=1
22764+
$echo "(I can't compile the test program--guessing unsigned.)" >&4
22765+
fi
22766+
$rm_try
22767+
2269822768
: see what type of char stdio uses.
2269922769
echo " "
2270022770
echo '#include <stdio.h>' | $cppstdin $cppminus > stdioh
@@ -25250,6 +25320,8 @@ srand48_r_proto='$srand48_r_proto'
2525025320
srandom_r_proto='$srandom_r_proto'
2525125321
src='$src'
2525225322
ssizetype='$ssizetype'
25323+
st_dev_sign='$st_dev_sign'
25324+
st_dev_size='$st_dev_size'
2525325325
st_ino_sign='$st_ino_sign'
2525425326
st_ino_size='$st_ino_size'
2525525327
startperl='$startperl'

Cross/config.sh-arm-linux

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,6 +1078,8 @@ srand48_r_proto='0'
10781078
srandom_r_proto='0'
10791079
src='.'
10801080
ssizetype='ssize_t'
1081+
st_dev_sign='1'
1082+
st_dev_size='4'
10811083
st_ino_sign='1'
10821084
st_ino_size='4'
10831085
startperl='#!/usr/bin/perl'

Cross/config.sh-arm-linux-n770

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,6 +1076,8 @@ srand48_r_proto='0'
10761076
srandom_r_proto='0'
10771077
src='.'
10781078
ssizetype='ssize_t'
1079+
st_dev_sign='1'
1080+
st_dev_size='4'
10791081
st_ino_sign='1'
10801082
st_ino_size='4'
10811083
startperl='#!/usr/bin/perl'

NetWare/config.wc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,6 +1042,8 @@ srand48_r_proto='0'
10421042
srandom_r_proto='0'
10431043
src=''
10441044
ssizetype='int'
1045+
st_dev_sign='1'
1046+
st_dev_size='4'
10451047
st_ino_sign='1'
10461048
st_ino_size='4'
10471049
startperl='#!perl'

Porting/config.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,6 +1107,8 @@ srand48_r_proto='REENTRANT_PROTO_I_LS'
11071107
srandom_r_proto='REENTRANT_PROTO_I_TS'
11081108
src='.'
11091109
ssizetype='ssize_t'
1110+
st_dev_sign='1'
1111+
st_dev_size='4'
11101112
st_ino_sign='1'
11111113
st_ino_size='8'
11121114
startperl='#!/opt/perl/bin/perl5.35.2'

config_h.SH

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4193,13 +4193,22 @@ sed <<!GROK!THIS! >$CONFIG_H -e 's!^#undef\(.*/\)\*!/\*#define\1 \*!' -e 's!^#un
41934193
*/
41944194
#define SELECT_MIN_BITS $selectminbits /**/
41954195
4196+
/* ST_DEV_SIZE:
4197+
* This variable contains the size of struct stat's st_dev in bytes.
4198+
*/
4199+
/* ST_DEV_SIGN:
4200+
* This symbol holds the signedness of struct stat's st_dev.
4201+
* 1 for unsigned, -1 for signed.
4202+
*/
41964203
/* ST_INO_SIZE:
41974204
* This variable contains the size of struct stat's st_ino in bytes.
41984205
*/
41994206
/* ST_INO_SIGN:
42004207
* This symbol holds the signedness of struct stat's st_ino.
42014208
* 1 for unsigned, -1 for signed.
42024209
*/
4210+
#define ST_DEV_SIGN $st_dev_sign /* st_dev sign */
4211+
#define ST_DEV_SIZE $st_dev_size /* st_dev size */
42034212
#define ST_INO_SIGN $st_ino_sign /* st_ino sign */
42044213
#define ST_INO_SIZE $st_ino_size /* st_ino size */
42054214

configure.com

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6970,6 +6970,8 @@ $ WC "src='" + src + "'"
69706970
$ WC "ssizetype='int'"
69716971
$ WC "startperl=" + startperl ! This one's special--no enclosing single quotes
69726972
$ WC "static_ext='" + static_ext + "'"
6973+
$ WC "st_dev_size='"4"'"
6974+
$ WC "st_dev_sign='1'"
69736975
$ WC "st_ino_size='" + st_ino_size + "'"
69746976
$ WC "st_ino_sign='1'"
69756977
$ WC "stdchar='" + stdchar + "'"

plan9/config_sh.sample

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,6 +1049,8 @@ srand48_r_proto='0'
10491049
srandom_r_proto='0'
10501050
src='.'
10511051
ssizetype='ssize_t'
1052+
st_dev_sign='1'
1053+
st_dev_size='4'
10521054
st_ino_sign='1'
10531055
st_ino_size='4'
10541056
startperl='#!/bin/perl'

pp_sys.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2915,7 +2915,38 @@ PP(pp_stat)
29152915
if (max) {
29162916
EXTEND(SP, max);
29172917
EXTEND_MORTAL(max);
2918+
#if ST_DEV_SIZE < IVSIZE || (ST_DEV_SIZE == IVSIZE && ST_DEV_SIGN < 0)
29182919
mPUSHi(PL_statcache.st_dev);
2920+
#elif ST_DEV_SIZE == IVSIZE
2921+
mPUSHu(PL_statcache.st_dev);
2922+
#else
2923+
# if ST_DEV_SIGN < 0
2924+
if (LIKELY((IV)PL_statcache.st_dev == PL_statcache.st_dev)) {
2925+
mPUSHi((IV)PL_statcache.st_dev);
2926+
}
2927+
# else
2928+
if (LIKELY((UV)PL_statcache.st_dev == PL_statcache.st_dev)) {
2929+
mPUSHu((UV)PL_statcache.st_dev);
2930+
}
2931+
# endif
2932+
else {
2933+
char buf[sizeof(PL_statcache.st_dev)*3+1];
2934+
/* sv_catpvf() casts 'j' size values down to IV, so it
2935+
isn't suitable for use here.
2936+
*/
2937+
# if defined(I_INTTYPES) && defined(HAS_SNPRINTF)
2938+
# if ST_DEV_SIGN < 0
2939+
int size = snprintf(buf, sizeof(buf), "%" PRIdMAX, (intmax_t)PL_statcache.st_dev);
2940+
# else
2941+
int size = snprintf(buf, sizeof(buf), "%" PRIuMAX, (uintmax_t)PL_statcache.st_dev);
2942+
# endif
2943+
STATIC_ASSERT_STMT(sizeof(intmax_t) >= sizeof(PL_statcache.st_dev));
2944+
mPUSHp(buf, size);
2945+
# else
2946+
# error extraordinarily large st_dev but no inttypes.h or no snprintf
2947+
# endif
2948+
}
2949+
#endif
29192950
{
29202951
/*
29212952
* We try to represent st_ino as a native IV or UV where

t/op/stat.t

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ if ($^O eq 'MSWin32') {
2929

3030
my $Errno_loaded = eval { require Errno };
3131

32-
plan tests => 110;
32+
plan tests => 111;
3333

3434
my $Perl = which_perl();
3535

@@ -664,6 +664,24 @@ SKIP: {
664664
unlink $link;
665665
}
666666

667+
SKIP:
668+
{
669+
# test needs a FreeBSD /usr/bin/stat
670+
# /tmp is typically tmpfs on a new FreeBSD
671+
$^O eq "freebsd"
672+
or skip "only checking freebsd for now", 1;
673+
-x "/usr/bin/stat"
674+
or skip "no /usr/bin/stat", 1;
675+
my @s = stat "/tmp";
676+
@s or skip "No /tmp found", 1;
677+
my $test = `/usr/bin/stat -f '%d %i' /tmp`;
678+
$test && $test =~ /^-?\d+ -?\d+/
679+
or skip "stat didn't return an expected result";
680+
chomp $test;
681+
is("$s[0] $s[1]", $test,
682+
"perl stat didn't match system stat utility");
683+
}
684+
667685
END {
668686
chmod 0666, $tmpfile;
669687
unlink_all $tmpfile;

uconfig.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4158,13 +4158,22 @@
41584158
*/
41594159
#define SELECT_MIN_BITS 32 /**/
41604160

4161+
/* ST_DEV_SIZE:
4162+
* This variable contains the size of struct stat's st_dev in bytes.
4163+
*/
4164+
/* ST_DEV_SIGN:
4165+
* This symbol holds the signedness of struct stat's st_dev.
4166+
* 1 for unsigned, -1 for signed.
4167+
*/
41614168
/* ST_INO_SIZE:
41624169
* This variable contains the size of struct stat's st_ino in bytes.
41634170
*/
41644171
/* ST_INO_SIGN:
41654172
* This symbol holds the signedness of struct stat's st_ino.
41664173
* 1 for unsigned, -1 for signed.
41674174
*/
4175+
#define ST_DEV_SIGN 1 /* st_dev sign */
4176+
#define ST_DEV_SIZE 4 /* st_dev size */
41684177
#define ST_INO_SIGN 1 /* st_ino sign */
41694178
#define ST_INO_SIZE 4 /* st_ino size */
41704179

@@ -5313,6 +5322,6 @@
53135322
#endif
53145323

53155324
/* Generated from:
5316-
* 55a531381747550c11c2c61b9a9da2dacde4df465b874df55a9c923e495deb3a config_h.SH
5317-
* 2fece1e405c60ae089fe55acaa42471b6fba78b7ab4cefc6d5e18a94b72fc2c4 uconfig.sh
5325+
* e953597150b0e47d177abc2f4dd1351a49557e0b537ff593acf85f76709bcce7 config_h.SH
5326+
* 801886c55550c022c8edfea3ef6873294a11a495843466cf80bd32f3bcee95e4 uconfig.sh
53185327
* ex: set ro: */

uconfig.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,8 @@ srand48_r_proto='0'
850850
srandom_r_proto='0'
851851
src='.'
852852
ssizetype=int
853+
st_dev_sign='1'
854+
st_dev_size='4'
853855
st_ino_sign='1'
854856
st_ino_size='4'
855857
startperl='#!perl'

uconfig64.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,8 @@ srand48_r_proto='0'
850850
srandom_r_proto='0'
851851
src='.'
852852
ssizetype=long
853+
st_dev_sign='1'
854+
st_dev_size='4'
853855
st_ino_sign='1'
854856
st_ino_size='8'
855857
startperl='#!perl'

win32/config.gc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,6 +1073,8 @@ srand48_r_proto='0'
10731073
srandom_r_proto='0'
10741074
src=''
10751075
ssizetype='int'
1076+
st_dev_sign='1'
1077+
st_dev_size='4'
10761078
st_ino_sign='1'
10771079
st_ino_size='4'
10781080
startperl='#!perl'

win32/config.vc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,6 +1072,8 @@ srand48_r_proto='0'
10721072
srandom_r_proto='0'
10731073
src=''
10741074
ssizetype='int'
1075+
st_dev_sign='1'
1076+
st_dev_size='4'
10751077
st_ino_sign='1'
10761078
st_ino_size='4'
10771079
startperl='#!perl'

0 commit comments

Comments
 (0)