summaryrefslogtreecommitdiff
path: root/pp_sys.c
diff options
context:
space:
mode:
authorTony Cook <tony@develop-help.com>2021-07-09 10:22:40 +1000
committerTony Cook <tony@develop-help.com>2021-09-01 10:59:44 +1000
commit9b5699737a6b587546239d586832f420cf7f2dea (patch)
tree37f4fec318866b00f20095ba66f60faa44788cd6 /pp_sys.c
parentb852e1da77b497e086508451bebff00541073fb1 (diff)
downloadperl-9b5699737a6b587546239d586832f420cf7f2dea.tar.gz
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.
Diffstat (limited to 'pp_sys.c')
-rw-r--r--pp_sys.c31
1 files changed, 31 insertions, 0 deletions
diff --git a/pp_sys.c b/pp_sys.c
index 9b2d64ae2e..cb449c3d9e 100644
--- a/pp_sys.c
+++ b/pp_sys.c
@@ -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