Coverage Report

Created: 2026-09-14 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/r-oss-fuzz/harnesses/datetime.c
Line
Count
Source
1
/*
2
 * libFuzzer harness for R's date/time parsing.
3
 *
4
 * Passes fuzzed input as a character string through strptime, as.Date,
5
 * as.POSIXct, and as.POSIXlt -- exercising R_strptime (Rstrptime.h) and
6
 * src/main/datetime.c: ~2000 lines of locale-dependent parsing, timezone
7
 * handling, leap seconds, and DST transitions.
8
 *
9
 * Adapted from r-afl's datetime harness.
10
 */
11
12
#include <stdint.h>
13
#include <string.h>
14
15
#include "common.h"
16
17
1.84k
#define FUZZ_MAX_INPUT (1024 * 64)
18
19
/*
20
 * strptime format strings, each exercising a different path in
21
 * Rstrptime.h's strptime_internal (numeric fields, locale month/weekday
22
 * names, ISO 8601 zone offset, locale preferred representations).
23
 */
24
18.4k
#define N_FORMATS 6
25
static const char *strptime_formats[N_FORMATS] = {
26
    "%Y-%m-%d %H:%M:%S",
27
    "%d/%b/%Y",
28
    "%Y-%m-%dT%H:%M:%S%z",
29
    "%c",
30
    "%x %X",
31
    "%a %d %b %Y",
32
};
33
34
18.4k
#define N_CALLS (N_FORMATS + 3)
35
36
static SEXP x_str;
37
static SEXP calls[N_CALLS];
38
39
int LLVMFuzzerInitialize(int *argc, char ***argv)
40
2
{
41
2
    fuzz_init_r();
42
43
2
    SEXP sym_strptime   = Rf_install("strptime");
44
2
    SEXP sym_as_Date    = Rf_install("as.Date");
45
2
    SEXP sym_as_POSIXct = Rf_install("as.POSIXct");
46
2
    SEXP sym_as_POSIXlt = Rf_install("as.POSIXlt");
47
48
    /* Reusable input container -- each iteration swaps its CHARSXP. */
49
2
    Rf_protect(x_str = Rf_allocVector(STRSXP, 1));
50
51
2
    SEXP tz_utc;
52
2
    Rf_protect(tz_utc = Rf_mkString("UTC"));
53
54
    /* strptime(x, format=fmt, tz="UTC") for each fixed format */
55
14
    for (int i = 0; i < N_FORMATS; i++) {
56
12
        SEXP fmt;
57
12
        Rf_protect(fmt = Rf_mkString(strptime_formats[i]));
58
12
        Rf_protect(calls[i] = Rf_lang4(sym_strptime, x_str, fmt, tz_utc));
59
12
        SET_TAG(CDDR(calls[i]), Rf_install("format"));
60
12
        SET_TAG(CDR(CDDR(calls[i])), Rf_install("tz"));
61
12
    }
62
63
    /* as.Date(x, format="%Y-%m-%d") */
64
2
    {
65
2
        SEXP date_fmt;
66
2
        Rf_protect(date_fmt = Rf_mkString("%Y-%m-%d"));
67
2
        Rf_protect(calls[N_FORMATS] = Rf_lang3(sym_as_Date, x_str, date_fmt));
68
2
        SET_TAG(CDDR(calls[N_FORMATS]), Rf_install("format"));
69
2
    }
70
71
    /* as.POSIXct(x, tz="UTC") */
72
2
    Rf_protect(calls[N_FORMATS + 1] = Rf_lang3(sym_as_POSIXct, x_str, tz_utc));
73
2
    SET_TAG(CDDR(calls[N_FORMATS + 1]), Rf_install("tz"));
74
75
    /* as.POSIXlt(x, tz="UTC") */
76
2
    Rf_protect(calls[N_FORMATS + 2] = Rf_lang3(sym_as_POSIXlt, x_str, tz_utc));
77
2
    SET_TAG(CDDR(calls[N_FORMATS + 2]), Rf_install("tz"));
78
79
    /* Warmup: prime datetime internals. */
80
2
    {
81
2
        int error = 0;
82
2
        SET_STRING_ELT(x_str, 0, Rf_mkChar("2024-01-15 12:30:00"));
83
20
        for (int i = 0; i < N_CALLS; i++) {
84
18
            R_tryEval(calls[i], R_GlobalEnv, &error);
85
18
            error = 0;
86
18
        }
87
2
    }
88
89
2
    return 0;
90
2
}
91
92
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
93
1.84k
{
94
1.84k
    if (size == 0 || size > FUZZ_MAX_INPUT)
95
7
        return 0;
96
97
1.84k
    char buf[FUZZ_MAX_INPUT + 1];
98
1.84k
    memcpy(buf, data, size);
99
1.84k
    buf[size] = '\0';
100
101
1.84k
    if (!fuzz_set_string(x_str, buf))
102
0
        return 0;
103
104
1.84k
    fuzz_eval_data_t ed = { .env = R_GlobalEnv };
105
18.4k
    for (int i = 0; i < N_CALLS; i++) {
106
16.5k
        ed.call = calls[i];
107
16.5k
        R_ToplevelExec(fuzz_do_eval, &ed);
108
16.5k
    }
109
110
1.84k
    return 0;
111
1.84k
}