Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sanitise results of stat()/lstat() on Solaris #504

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
Comment on lines +155 to +186
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this function is utilized solely in scenarios where #if defined __sun && defined HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC is true, it would be more straightforward to not define it when this condition is not met.

E.g.,

#if defined __sun && defined HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
int stat_time_normalize(int result, STRUCT_STAT *st)
{
    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;
        }
    }
    return result;
}
#endif

Loading