Skip to content

Instantly share code, notes, and snippets.

@mildsunrise
Last active September 8, 2024 12:25
Show Gist options
  • Save mildsunrise/c63505931534bd3c0e143c0db8cad3f3 to your computer and use it in GitHub Desktop.
Save mildsunrise/c63505931534bd3c0e143c0db8cad3f3 to your computer and use it in GitHub Desktop.
dumps the data in the vdso_data VVAR (timekeeping info for gettimeofday vDSO)
// WARNING: only for x86
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include <sys/auxv.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/prctl.h>
#include <linux/seccomp.h>
#include <x86intrin.h>
// copied from <linux/time.h>
#define CLOCK_REALTIME 0
#define CLOCK_MONOTONIC 1
#define CLOCK_PROCESS_CPUTIME_ID 2
#define CLOCK_THREAD_CPUTIME_ID 3
#define CLOCK_MONOTONIC_RAW 4
#define CLOCK_REALTIME_COARSE 5
#define CLOCK_MONOTONIC_COARSE 6
#define CLOCK_BOOTTIME 7
#define CLOCK_REALTIME_ALARM 8
#define CLOCK_BOOTTIME_ALARM 9
#define CLOCK_SGI_CYCLE 10
#define CLOCK_TAI 11
#define MAX_CLOCKS 16
#define CLOCKS_MASK (CLOCK_REALTIME | CLOCK_MONOTONIC)
#define CLOCKS_MONO CLOCK_MONOTONIC
// adapted from include/vdso/datapage.h
#include <linux/types.h>
struct arch_vdso_data {};
#define VDSO_BASES (CLOCK_TAI + 1)
#define VDSO_HRES (BIT(CLOCK_REALTIME) | \
BIT(CLOCK_MONOTONIC) | \
BIT(CLOCK_BOOTTIME) | \
BIT(CLOCK_TAI))
#define VDSO_COARSE (BIT(CLOCK_REALTIME_COARSE) | \
BIT(CLOCK_MONOTONIC_COARSE))
#define VDSO_RAW (BIT(CLOCK_MONOTONIC_RAW))
#define CS_HRES_COARSE 0
#define CS_RAW 1
#define CS_BASES (CS_RAW + 1)
/**
* struct vdso_timestamp - basetime per clock_id
* @sec: seconds
* @nsec: nanoseconds
*
* There is one vdso_timestamp object in vvar for each vDSO-accelerated
* clock_id. For high-resolution clocks, this encodes the time
* corresponding to vdso_data.cycle_last. For coarse clocks this encodes
* the actual time.
*
* To be noticed that for highres clocks nsec is left-shifted by
* vdso_data.cs[x].shift.
*/
struct vdso_timestamp {
__u64 sec;
__u64 nsec;
};
/**
* struct vdso_data - vdso datapage representation
* @seq: timebase sequence counter
* @clock_mode: clock mode
* @cycle_last: timebase at clocksource init
* @mask: clocksource mask
* @mult: clocksource multiplier
* @shift: clocksource shift
* @basetime[clock_id]: basetime per clock_id
* @offset[clock_id]: time namespace offset per clock_id
* @tz_minuteswest: minutes west of Greenwich
* @tz_dsttime: type of DST correction
* @hrtimer_res: hrtimer resolution
* @__unused: unused
* @arch_data: architecture specific data (optional, defaults
* to an empty struct)
*
* vdso_data will be accessed by 64 bit and compat code at the same time
* so we should be careful before modifying this structure.
*
* @basetime is used to store the base time for the system wide time getter
* VVAR page.
*
* @offset is used by the special time namespace VVAR pages which are
* installed instead of the real VVAR page. These namespace pages must set
* @seq to 1 and @clock_mode to VDSO_CLOCKMODE_TIMENS to force the code into
* the time namespace slow path. The namespace aware functions retrieve the
* real system wide VVAR page, read host time and add the per clock offset.
* For clocks which are not affected by time namespace adjustment the
* offset must be zero.
*/
struct vdso_data {
__u32 seq;
__s32 clock_mode;
__u64 cycle_last;
__u64 mask;
__u32 mult;
__u32 shift;
union {
struct vdso_timestamp basetime[VDSO_BASES];
//struct timens_offset offset[VDSO_BASES];
};
__s32 tz_minuteswest;
__s32 tz_dsttime;
__u32 hrtimer_res;
__u32 __unused;
struct arch_vdso_data arch_data;
};
// main code
uintptr_t get_vvar_address() {
// quickly parse /proc/self/maps to find [vvar] mapping
char mmaps [4096*4];
FILE* mmapsfile = fopen("/proc/self/maps", "r");
if (!mmapsfile) {
fprintf(stderr, "could not access own maps\n");
exit(1);
}
size_t nread = fread(mmaps, 1, sizeof(mmaps), mmapsfile);
assert(nread > 0 && nread < sizeof(mmaps));
fclose(mmapsfile);
mmaps[nread] = 0;
for (char* line = mmaps; line != NULL;) {
char* next_line = strchr(line, '\n');
if (next_line != NULL) *(next_line++) = 0;
if (strstr(line, "[vvar]"))
return strtol(line, NULL, 16);
line = next_line;
}
fprintf(stderr, "could not find [vvar] mapping\n");
exit(1);
}
const char* CLOCKSOURCE_NAMES [CS_BASES] = {
"HRES_COARSE",
"RAW",
};
const char* CLOCK_NAMES [VDSO_BASES] = {
"REALTIME",
"MONOTONIC",
"PROCESS_CPUTIME_ID",
"THREAD_CPUTIME_ID",
"MONOTONIC_RAW",
"REALTIME_COARSE",
"MONOTONIC_COARSE",
"BOOTTIME",
"REALTIME_ALARM",
"BOOTTIME_ALARM",
"SGI_CYCLE",
"TAI",
};
void print_vdso_timestamp(const struct vdso_timestamp* vs) {
__u64 __time = vs->sec;
__u64 secs = __time % 60; __time /= 60;
__u64 mins = __time % 60; __time /= 60;
__u64 hours = __time % 24; __time /= 24;
__u64 days = __time;
printf("%5llu days, %2llu:%02llu:%02llu.%09llu", days, hours, mins, secs, vs->nsec);
}
int main() {
uintptr_t vvar_addr = get_vvar_address();
printf("vvar address: %p\n", (void*)vvar_addr);
// printf("Entering strict seccomp...\n");
// prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT);
printf("\n\n1. dumping VVAR data...\n");
const struct vdso_data *_vdso_data = (const struct vdso_data *)(vvar_addr + 128);
for (size_t i = 0; i < CS_BASES; i++) {
const struct vdso_data *vd = _vdso_data + i;
printf("\n_vdso_data[%s] = {\n", CLOCKSOURCE_NAMES[i]);
printf(" seq = %u\n", vd->seq);
printf("\n");
printf(" clock_mode = %d\n", vd->clock_mode);
printf(" cycle_last = %llu\n", vd->cycle_last);
printf(" mask = %llu\n", vd->mask);
printf(" mult = %u\n", vd->mult);
printf(" shift = %u\n", vd->shift);
printf("\n");
for (size_t i = 0; i < VDSO_BASES; i++) {
printf(" basetime[%s] = ", CLOCK_NAMES[i]);
print_vdso_timestamp(&vd->basetime[i]);
printf("\n");
}
printf("\n");
printf(" tz_minuteswest = %d\n", vd->tz_minuteswest);
printf(" tz_dsttime = %d\n", vd->tz_dsttime);
printf(" hrtimer_res = %u\n", vd->hrtimer_res);
printf(" __unused = %u\n", vd->__unused);
printf("}\n");
}
printf("\n\n2. attempting to read TSC...\n\n");
printf("TSC read correctly: %llu\n", __rdtsc());
printf("\n\n3. attempting to call clock_gettime...\n\n");
struct timespec ctime = {0, 0};
if (!clock_gettime(CLOCK_REALTIME, &ctime)) {
print_vdso_timestamp((struct vdso_timestamp*)&ctime);
printf("\n");
} else {
printf("could NOT read clock_gettime\n");
}
// libc is not seccomp-friendly and turns exit() into exit_group()
syscall(SYS_exit, 0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment