Skip to content

Commit

Permalink
sanitise results of stat()/lstat() on Solaris
Browse files Browse the repository at this point in the history
Solaris 11 (possibly only 11.4) can return negative values for
st_atim.tv_nsec, st_mtim.tv_nsec and st_ctim.tv_nsec.

Gnulib already identifies this as an issue and works around it with
stat_time_normalize().  I've adapted that function for this patch,
omitting st_ctim (unused by rsync) and using only a very simple test for
time_t overflow.

The feature test macros used mean this code path will be used on _all_
Solaris builds, but this shouldn't be an issue; it would probably be
safe on anything that provides tv_nsec.
  • Loading branch information
Ciaran Anscomb committed Jul 7, 2023
1 parent 2f9b963 commit 93296b8
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 4 deletions.
18 changes: 14 additions & 4 deletions syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -357,19 +357,29 @@ int do_mkstemp(char *template, mode_t perms)
int do_stat(const char *path, STRUCT_STAT *st)
{
#ifdef USE_STAT64_FUNCS
return stat64(path, st);
int r = stat64(path, st);
#else
return stat(path, st);
int r = stat(path, st);
#endif
#if defined __sun && defined HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
return stat_time_normalize(r, st);
#else
return r;
#endif
}

int do_lstat(const char *path, STRUCT_STAT *st)
{
#ifdef SUPPORT_LINKS
# ifdef USE_STAT64_FUNCS
return lstat64(path, st);
int r = lstat64(path, st);
# else
int r = lstat(path, st);
# endif
# if defined __sun && defined HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
return stat_time_normalize(r, st);
# else
return lstat(path, st);
return r;
# endif
#else
return do_stat(path, st);
Expand Down
41 changes: 41 additions & 0 deletions util2.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,44 @@ const char *src_file(const char *file)
return file + prefix;
return file;
}

/* If a stat-like function returned RESULT, normalize the timestamps
in *ST, in case this platform suffers from the Solaris 11 bug where
tv_nsec might be negative. Return the adjusted RESULT, setting
errno to EOVERFLOW if normalization overflowed. This function
is intended to be private to this .h file.
This is adapted from Gnulib. */

int stat_time_normalize(int result, STRUCT_STAT *st)
{
#if defined __sun && defined HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
if (result == 0) {
long int timespec_hz = 1000000000;
short int const ts_off[] = { offsetof (struct stat, st_atim),
offsetof (struct stat, st_mtim) };
unsigned i;
for (i = 0; i < sizeof ts_off / sizeof *ts_off; i++) {
struct timespec *ts = (struct timespec *)((char *) st + ts_off[i]);
long int q = ts->tv_nsec / timespec_hz;
long int r = ts->tv_nsec % timespec_hz;
if (r < 0) {
r += timespec_hz;
q--;
}
ts->tv_nsec = r;
/* Overflow is possible, as Solaris 11 stat can yield
tv_sec == TYPE_MINIMUM (time_t) && tv_nsec == -1000000000. */
time_t sec = ts->tv_sec + q;
if ((q > 0 && sec < ts->tv_sec) || (q < 0 && sec > ts->tv_sec)) {
errno = EOVERFLOW;
return -1;
}
ts->tv_sec = sec;
}
}
#else
(void)st;
#endif
return result;
}

0 comments on commit 93296b8

Please sign in to comment.