mirror of
https://git.sr.ht/~cismonx/bookmarkfs
synced 2025-06-07 19:58:50 +00:00
backend: prevent timestamp overflow
When updating timestamps, make sure that the corresponding microsecond value fits in a single signed 64-bit integer, so that it won't result in an integer overflow, which is UB. Also forbid timestamps before the Unix epoch, since working with negative time_t is problematic. This check does not apply to current timestamp, however, add a check on backend startup to ensure sane system time. There's no need to validate `tv_nsec`, since the kernel already does that for us.
This commit is contained in:
parent
09e186b348
commit
750c16077c
3 changed files with 38 additions and 11 deletions
|
@ -772,13 +772,8 @@ build_tsnode (
|
||||||
ts = &now;
|
ts = &now;
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: May overflow if system time is badly wrong,
|
|
||||||
// but don't bother to check.
|
|
||||||
time_t secs = ts->tv_sec + EPOCH_DIFF;
|
time_t secs = ts->tv_sec + EPOCH_DIFF;
|
||||||
int64_t microsecs = secs * 1000000 + ts->tv_nsec / 1000;
|
int64_t microsecs = secs * 1000000 + ts->tv_nsec / 1000;
|
||||||
if (microsecs < 0) {
|
|
||||||
microsecs = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
char buf[32];
|
char buf[32];
|
||||||
int nbytes = snprintf(buf, sizeof(buf), "%" PRIi64, microsecs);
|
int nbytes = snprintf(buf, sizeof(buf), "%" PRIi64, microsecs);
|
||||||
|
@ -1713,6 +1708,13 @@ static int
|
||||||
backend_init (
|
backend_init (
|
||||||
uint32_t flags
|
uint32_t flags
|
||||||
) {
|
) {
|
||||||
|
struct timespec now;
|
||||||
|
xgetrealtime(&now);
|
||||||
|
if (!valid_ts_sec(now.tv_sec)) {
|
||||||
|
log_puts("bad system time");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(flags & BOOKMARKFS_BACKEND_LIB_READY)) {
|
if (!(flags & BOOKMARKFS_BACKEND_LIB_READY)) {
|
||||||
if (0 != bookmarkfs_lib_init()) {
|
if (0 != bookmarkfs_lib_init()) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -2517,10 +2519,16 @@ bookmark_set (
|
||||||
struct timespec const *times = val;
|
struct timespec const *times = val;
|
||||||
json_t *ts_node;
|
json_t *ts_node;
|
||||||
if (flags & BOOKMARK_FLAG(SET_ATIME)) {
|
if (flags & BOOKMARK_FLAG(SET_ATIME)) {
|
||||||
|
if (!valid_ts_sec(times[0].tv_sec)) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
build_tsnode(×[0], &ts_node);
|
build_tsnode(×[0], &ts_node);
|
||||||
json_object_sset_new(node, "date_last_used", ts_node);
|
json_object_sset_new(node, "date_last_used", ts_node);
|
||||||
}
|
}
|
||||||
if (flags & BOOKMARK_FLAG(SET_MTIME)) {
|
if (flags & BOOKMARK_FLAG(SET_MTIME)) {
|
||||||
|
if (!valid_ts_sec(times[1].tv_sec)) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
build_tsnode(×[1], &ts_node);
|
build_tsnode(×[1], &ts_node);
|
||||||
json_object_sset_new(node, "date_modified", ts_node);
|
json_object_sset_new(node, "date_modified", ts_node);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2038,12 +2038,7 @@ static int64_t
|
||||||
timespec_to_usecs (
|
timespec_to_usecs (
|
||||||
struct timespec const *ts
|
struct timespec const *ts
|
||||||
) {
|
) {
|
||||||
int64_t microsecs = ts->tv_sec * 1000000 + ts->tv_nsec / 1000;
|
return ts->tv_sec * 1000000 + ts->tv_nsec / 1000;
|
||||||
|
|
||||||
if (microsecs < 0) {
|
|
||||||
microsecs = 0;
|
|
||||||
}
|
|
||||||
return microsecs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -3003,6 +2998,13 @@ static int
|
||||||
backend_init (
|
backend_init (
|
||||||
uint32_t flags
|
uint32_t flags
|
||||||
) {
|
) {
|
||||||
|
struct timespec now;
|
||||||
|
xgetrealtime(&now);
|
||||||
|
if (!valid_ts_sec(now.tv_sec)) {
|
||||||
|
log_puts("bad system time");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(flags & BOOKMARKFS_BACKEND_LIB_READY)
|
if (!(flags & BOOKMARKFS_BACKEND_LIB_READY)
|
||||||
&& !(flags & BOOKMARKFS_FRONTEND_MKFS)
|
&& !(flags & BOOKMARKFS_FRONTEND_MKFS)
|
||||||
) {
|
) {
|
||||||
|
@ -3675,10 +3677,16 @@ bookmark_set (
|
||||||
if (flags & BOOKMARK_FLAG(SET_ATIME, SET_MTIME)) {
|
if (flags & BOOKMARK_FLAG(SET_ATIME, SET_MTIME)) {
|
||||||
struct timespec const *times = val;
|
struct timespec const *times = val;
|
||||||
if (flags & BOOKMARK_FLAG(SET_ATIME)) {
|
if (flags & BOOKMARK_FLAG(SET_ATIME)) {
|
||||||
|
if (!valid_ts_sec(times[0].tv_sec)) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
place_cols.last_visit_date = timespec_to_usecs(×[0]);
|
place_cols.last_visit_date = timespec_to_usecs(×[0]);
|
||||||
--xattr_id;
|
--xattr_id;
|
||||||
}
|
}
|
||||||
if (flags & BOOKMARK_FLAG(SET_MTIME)) {
|
if (flags & BOOKMARK_FLAG(SET_MTIME)) {
|
||||||
|
if (!valid_ts_sec(times[1].tv_sec)) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
bm_cols.last_modified = timespec_to_usecs(×[1]);
|
bm_cols.last_modified = timespec_to_usecs(×[1]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -57,6 +57,17 @@
|
||||||
#define FILENAME_BADLEN -2
|
#define FILENAME_BADLEN -2
|
||||||
#define FILENAME_DOTDOT -3
|
#define FILENAME_DOTDOT -3
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum valid seconds that a signed 64-bit integer can hold
|
||||||
|
* when converted to microseconds.
|
||||||
|
*
|
||||||
|
* Equals to `INT64_MAX / 1000000 - EPOCH_DIFF - 1`:
|
||||||
|
* - See the Chromium backend for `EPOCH_DIFF`.
|
||||||
|
* - Considering `tv_nsec`, subtract another second.
|
||||||
|
*/
|
||||||
|
#define TIMESPEC_SEC_MAX INT64_C(9211727563253)
|
||||||
|
#define valid_ts_sec(sec) ((sec) >= 0 && (sec) <= TIMESPEC_SEC_MAX)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the parent directory of a file, and stores its basename
|
* Opens the parent directory of a file, and stores its basename
|
||||||
* to `basename_ptr`.
|
* to `basename_ptr`.
|
||||||
|
|
Loading…
Add table
Reference in a new issue