Coverage Report

Created: 2026-10-16 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/r-oss-fuzz/harnesses/common.h
Line
Count
Source
1
/*
2
 * Shared helpers for R fuzzing harnesses (libFuzzer).
3
 *
4
 * Provides:
5
 *   fuzz_suppress_warnings() - Suppress R warnings to avoid buffer overflow
6
 *   fuzz_init_r()            - Full R initialization sequence
7
 *   fuzz_set_string()        - Stage a string input under a toplevel context
8
 *   fuzz_set_string_ce()     - Stage a string with an explicit encoding mark
9
 *   fuzz_set_raw_arg()       - Stage a raw-vector input likewise
10
 *   fuzz_make_wrapper()      - Parse an R closure that implements a call slot
11
 *   fuzz_scratch_file()      - Per-process temp file for path-only entry points
12
 *   fuzz_write_scratch()     - Rewrite that file with the current input
13
 *   fuzz_repeat_product_excessive() - Guard against TRE repeat blowup
14
 *
15
 * Adapted from r-afl's fuzz.h for use with libFuzzer instead of AFL++.
16
 */
17
18
#ifndef FUZZ_COMMON_H
19
#define FUZZ_COMMON_H
20
21
#include <limits.h>
22
#include <stdint.h>
23
#include <stdio.h>
24
#include <stdlib.h>
25
#include <string.h>
26
#include <unistd.h>
27
28
#define R_NO_REMAP 1
29
#include <Rembedded.h>
30
#include <Rinternals.h>
31
#include <Rinterface.h>   /* R_SignalHandlers */
32
#include <R_ext/Parse.h>
33
34
/*
35
 * Suppress R warnings globally.
36
 *
37
 * Many R functions emit warnings on invalid input (e.g., "NAs introduced
38
 * by coercion").  In persistent fuzzing, these accumulate and can overflow
39
 * R's warning buffer, triggering fatal errors.  Setting warn = -1
40
 * suppresses all warnings.
41
 */
42
static void fuzz_suppress_warnings(void)
43
32
{
44
32
    int error = 0;
45
32
    SEXP warn_call;
46
32
    Rf_protect(warn_call = Rf_lang2(Rf_install("options"),
47
32
                                    Rf_ScalarInteger(-1)));
48
32
    SET_TAG(CDR(warn_call), Rf_install("warn"));
49
32
    R_tryEval(warn_call, R_GlobalEnv, &error);
50
32
    Rf_unprotect(1);
51
32
}
io.c:fuzz_suppress_warnings
Line
Count
Source
43
2
{
44
2
    int error = 0;
45
2
    SEXP warn_call;
46
2
    Rf_protect(warn_call = Rf_lang2(Rf_install("options"),
47
2
                                    Rf_ScalarInteger(-1)));
48
2
    SET_TAG(CDR(warn_call), Rf_install("warn"));
49
2
    R_tryEval(warn_call, R_GlobalEnv, &error);
50
2
    Rf_unprotect(1);
51
2
}
dcf.c:fuzz_suppress_warnings
Line
Count
Source
43
2
{
44
2
    int error = 0;
45
2
    SEXP warn_call;
46
2
    Rf_protect(warn_call = Rf_lang2(Rf_install("options"),
47
2
                                    Rf_ScalarInteger(-1)));
48
2
    SET_TAG(CDR(warn_call), Rf_install("warn"));
49
2
    R_tryEval(warn_call, R_GlobalEnv, &error);
50
2
    Rf_unprotect(1);
51
2
}
character.c:fuzz_suppress_warnings
Line
Count
Source
43
2
{
44
2
    int error = 0;
45
2
    SEXP warn_call;
46
2
    Rf_protect(warn_call = Rf_lang2(Rf_install("options"),
47
2
                                    Rf_ScalarInteger(-1)));
48
2
    SET_TAG(CDR(warn_call), Rf_install("warn"));
49
2
    R_tryEval(warn_call, R_GlobalEnv, &error);
50
2
    Rf_unprotect(1);
51
2
}
encoding.c:fuzz_suppress_warnings
Line
Count
Source
43
2
{
44
2
    int error = 0;
45
2
    SEXP warn_call;
46
2
    Rf_protect(warn_call = Rf_lang2(Rf_install("options"),
47
2
                                    Rf_ScalarInteger(-1)));
48
2
    SET_TAG(CDR(warn_call), Rf_install("warn"));
49
2
    R_tryEval(warn_call, R_GlobalEnv, &error);
50
2
    Rf_unprotect(1);
51
2
}
sort.c:fuzz_suppress_warnings
Line
Count
Source
43
2
{
44
2
    int error = 0;
45
2
    SEXP warn_call;
46
2
    Rf_protect(warn_call = Rf_lang2(Rf_install("options"),
47
2
                                    Rf_ScalarInteger(-1)));
48
2
    SET_TAG(CDR(warn_call), Rf_install("warn"));
49
2
    R_tryEval(warn_call, R_GlobalEnv, &error);
50
2
    Rf_unprotect(1);
51
2
}
connections.c:fuzz_suppress_warnings
Line
Count
Source
43
2
{
44
2
    int error = 0;
45
2
    SEXP warn_call;
46
2
    Rf_protect(warn_call = Rf_lang2(Rf_install("options"),
47
2
                                    Rf_ScalarInteger(-1)));
48
2
    SET_TAG(CDR(warn_call), Rf_install("warn"));
49
2
    R_tryEval(warn_call, R_GlobalEnv, &error);
50
2
    Rf_unprotect(1);
51
2
}
unserialize.c:fuzz_suppress_warnings
Line
Count
Source
43
2
{
44
2
    int error = 0;
45
2
    SEXP warn_call;
46
2
    Rf_protect(warn_call = Rf_lang2(Rf_install("options"),
47
2
                                    Rf_ScalarInteger(-1)));
48
2
    SET_TAG(CDR(warn_call), Rf_install("warn"));
49
2
    R_tryEval(warn_call, R_GlobalEnv, &error);
50
2
    Rf_unprotect(1);
51
2
}
decompress.c:fuzz_suppress_warnings
Line
Count
Source
43
2
{
44
2
    int error = 0;
45
2
    SEXP warn_call;
46
2
    Rf_protect(warn_call = Rf_lang2(Rf_install("options"),
47
2
                                    Rf_ScalarInteger(-1)));
48
2
    SET_TAG(CDR(warn_call), Rf_install("warn"));
49
2
    R_tryEval(warn_call, R_GlobalEnv, &error);
50
2
    Rf_unprotect(1);
51
2
}
format.c:fuzz_suppress_warnings
Line
Count
Source
43
2
{
44
2
    int error = 0;
45
2
    SEXP warn_call;
46
2
    Rf_protect(warn_call = Rf_lang2(Rf_install("options"),
47
2
                                    Rf_ScalarInteger(-1)));
48
2
    SET_TAG(CDR(warn_call), Rf_install("warn"));
49
2
    R_tryEval(warn_call, R_GlobalEnv, &error);
50
2
    Rf_unprotect(1);
51
2
}
datetime.c:fuzz_suppress_warnings
Line
Count
Source
43
2
{
44
2
    int error = 0;
45
2
    SEXP warn_call;
46
2
    Rf_protect(warn_call = Rf_lang2(Rf_install("options"),
47
2
                                    Rf_ScalarInteger(-1)));
48
2
    SET_TAG(CDR(warn_call), Rf_install("warn"));
49
2
    R_tryEval(warn_call, R_GlobalEnv, &error);
50
2
    Rf_unprotect(1);
51
2
}
coerce.c:fuzz_suppress_warnings
Line
Count
Source
43
2
{
44
2
    int error = 0;
45
2
    SEXP warn_call;
46
2
    Rf_protect(warn_call = Rf_lang2(Rf_install("options"),
47
2
                                    Rf_ScalarInteger(-1)));
48
2
    SET_TAG(CDR(warn_call), Rf_install("warn"));
49
2
    R_tryEval(warn_call, R_GlobalEnv, &error);
50
2
    Rf_unprotect(1);
51
2
}
load.c:fuzz_suppress_warnings
Line
Count
Source
43
2
{
44
2
    int error = 0;
45
2
    SEXP warn_call;
46
2
    Rf_protect(warn_call = Rf_lang2(Rf_install("options"),
47
2
                                    Rf_ScalarInteger(-1)));
48
2
    SET_TAG(CDR(warn_call), Rf_install("warn"));
49
2
    R_tryEval(warn_call, R_GlobalEnv, &error);
50
2
    Rf_unprotect(1);
51
2
}
scan.c:fuzz_suppress_warnings
Line
Count
Source
43
2
{
44
2
    int error = 0;
45
2
    SEXP warn_call;
46
2
    Rf_protect(warn_call = Rf_lang2(Rf_install("options"),
47
2
                                    Rf_ScalarInteger(-1)));
48
2
    SET_TAG(CDR(warn_call), Rf_install("warn"));
49
2
    R_tryEval(warn_call, R_GlobalEnv, &error);
50
2
    Rf_unprotect(1);
51
2
}
parse.c:fuzz_suppress_warnings
Line
Count
Source
43
2
{
44
2
    int error = 0;
45
2
    SEXP warn_call;
46
2
    Rf_protect(warn_call = Rf_lang2(Rf_install("options"),
47
2
                                    Rf_ScalarInteger(-1)));
48
2
    SET_TAG(CDR(warn_call), Rf_install("warn"));
49
2
    R_tryEval(warn_call, R_GlobalEnv, &error);
50
2
    Rf_unprotect(1);
51
2
}
sprintf.c:fuzz_suppress_warnings
Line
Count
Source
43
2
{
44
2
    int error = 0;
45
2
    SEXP warn_call;
46
2
    Rf_protect(warn_call = Rf_lang2(Rf_install("options"),
47
2
                                    Rf_ScalarInteger(-1)));
48
2
    SET_TAG(CDR(warn_call), Rf_install("warn"));
49
2
    R_tryEval(warn_call, R_GlobalEnv, &error);
50
2
    Rf_unprotect(1);
51
2
}
zip.c:fuzz_suppress_warnings
Line
Count
Source
43
2
{
44
2
    int error = 0;
45
2
    SEXP warn_call;
46
2
    Rf_protect(warn_call = Rf_lang2(Rf_install("options"),
47
2
                                    Rf_ScalarInteger(-1)));
48
2
    SET_TAG(CDR(warn_call), Rf_install("warn"));
49
2
    R_tryEval(warn_call, R_GlobalEnv, &error);
50
2
    Rf_unprotect(1);
51
2
}
52
53
/*
54
 * Set R_HOME based on the fuzzer binary's location.
55
 *
56
 * OSS-Fuzz places everything in $OUT.  The build.sh installs R to
57
 * $OUT/r-install, so R_HOME is at $OUT/r-install/lib/R.  We derive
58
 * this from /proc/self/exe so it works regardless of the mount path.
59
 */
60
static void fuzz_set_r_home(void)
61
32
{
62
    /* Skip if R_HOME is already set (e.g., for local testing) */
63
32
    if (getenv("R_HOME") != NULL)
64
16
        return;
65
66
16
    char exe[PATH_MAX];
67
16
    ssize_t len = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
68
16
    if (len <= 0)
69
0
        return;
70
16
    exe[len] = '\0';
71
72
    /* Find the directory containing the binary */
73
16
    char *slash = strrchr(exe, '/');
74
16
    if (slash == NULL)
75
0
        return;
76
16
    *slash = '\0';
77
78
16
    char r_home[PATH_MAX];
79
16
    snprintf(r_home, sizeof(r_home), "%s/r-install/lib/R", exe);
80
16
    setenv("R_HOME", r_home, 1);
81
16
}
io.c:fuzz_set_r_home
Line
Count
Source
61
2
{
62
    /* Skip if R_HOME is already set (e.g., for local testing) */
63
2
    if (getenv("R_HOME") != NULL)
64
1
        return;
65
66
1
    char exe[PATH_MAX];
67
1
    ssize_t len = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
68
1
    if (len <= 0)
69
0
        return;
70
1
    exe[len] = '\0';
71
72
    /* Find the directory containing the binary */
73
1
    char *slash = strrchr(exe, '/');
74
1
    if (slash == NULL)
75
0
        return;
76
1
    *slash = '\0';
77
78
1
    char r_home[PATH_MAX];
79
1
    snprintf(r_home, sizeof(r_home), "%s/r-install/lib/R", exe);
80
1
    setenv("R_HOME", r_home, 1);
81
1
}
dcf.c:fuzz_set_r_home
Line
Count
Source
61
2
{
62
    /* Skip if R_HOME is already set (e.g., for local testing) */
63
2
    if (getenv("R_HOME") != NULL)
64
1
        return;
65
66
1
    char exe[PATH_MAX];
67
1
    ssize_t len = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
68
1
    if (len <= 0)
69
0
        return;
70
1
    exe[len] = '\0';
71
72
    /* Find the directory containing the binary */
73
1
    char *slash = strrchr(exe, '/');
74
1
    if (slash == NULL)
75
0
        return;
76
1
    *slash = '\0';
77
78
1
    char r_home[PATH_MAX];
79
1
    snprintf(r_home, sizeof(r_home), "%s/r-install/lib/R", exe);
80
1
    setenv("R_HOME", r_home, 1);
81
1
}
character.c:fuzz_set_r_home
Line
Count
Source
61
2
{
62
    /* Skip if R_HOME is already set (e.g., for local testing) */
63
2
    if (getenv("R_HOME") != NULL)
64
1
        return;
65
66
1
    char exe[PATH_MAX];
67
1
    ssize_t len = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
68
1
    if (len <= 0)
69
0
        return;
70
1
    exe[len] = '\0';
71
72
    /* Find the directory containing the binary */
73
1
    char *slash = strrchr(exe, '/');
74
1
    if (slash == NULL)
75
0
        return;
76
1
    *slash = '\0';
77
78
1
    char r_home[PATH_MAX];
79
1
    snprintf(r_home, sizeof(r_home), "%s/r-install/lib/R", exe);
80
1
    setenv("R_HOME", r_home, 1);
81
1
}
encoding.c:fuzz_set_r_home
Line
Count
Source
61
2
{
62
    /* Skip if R_HOME is already set (e.g., for local testing) */
63
2
    if (getenv("R_HOME") != NULL)
64
1
        return;
65
66
1
    char exe[PATH_MAX];
67
1
    ssize_t len = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
68
1
    if (len <= 0)
69
0
        return;
70
1
    exe[len] = '\0';
71
72
    /* Find the directory containing the binary */
73
1
    char *slash = strrchr(exe, '/');
74
1
    if (slash == NULL)
75
0
        return;
76
1
    *slash = '\0';
77
78
1
    char r_home[PATH_MAX];
79
1
    snprintf(r_home, sizeof(r_home), "%s/r-install/lib/R", exe);
80
1
    setenv("R_HOME", r_home, 1);
81
1
}
sort.c:fuzz_set_r_home
Line
Count
Source
61
2
{
62
    /* Skip if R_HOME is already set (e.g., for local testing) */
63
2
    if (getenv("R_HOME") != NULL)
64
1
        return;
65
66
1
    char exe[PATH_MAX];
67
1
    ssize_t len = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
68
1
    if (len <= 0)
69
0
        return;
70
1
    exe[len] = '\0';
71
72
    /* Find the directory containing the binary */
73
1
    char *slash = strrchr(exe, '/');
74
1
    if (slash == NULL)
75
0
        return;
76
1
    *slash = '\0';
77
78
1
    char r_home[PATH_MAX];
79
1
    snprintf(r_home, sizeof(r_home), "%s/r-install/lib/R", exe);
80
1
    setenv("R_HOME", r_home, 1);
81
1
}
connections.c:fuzz_set_r_home
Line
Count
Source
61
2
{
62
    /* Skip if R_HOME is already set (e.g., for local testing) */
63
2
    if (getenv("R_HOME") != NULL)
64
1
        return;
65
66
1
    char exe[PATH_MAX];
67
1
    ssize_t len = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
68
1
    if (len <= 0)
69
0
        return;
70
1
    exe[len] = '\0';
71
72
    /* Find the directory containing the binary */
73
1
    char *slash = strrchr(exe, '/');
74
1
    if (slash == NULL)
75
0
        return;
76
1
    *slash = '\0';
77
78
1
    char r_home[PATH_MAX];
79
1
    snprintf(r_home, sizeof(r_home), "%s/r-install/lib/R", exe);
80
1
    setenv("R_HOME", r_home, 1);
81
1
}
unserialize.c:fuzz_set_r_home
Line
Count
Source
61
2
{
62
    /* Skip if R_HOME is already set (e.g., for local testing) */
63
2
    if (getenv("R_HOME") != NULL)
64
1
        return;
65
66
1
    char exe[PATH_MAX];
67
1
    ssize_t len = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
68
1
    if (len <= 0)
69
0
        return;
70
1
    exe[len] = '\0';
71
72
    /* Find the directory containing the binary */
73
1
    char *slash = strrchr(exe, '/');
74
1
    if (slash == NULL)
75
0
        return;
76
1
    *slash = '\0';
77
78
1
    char r_home[PATH_MAX];
79
1
    snprintf(r_home, sizeof(r_home), "%s/r-install/lib/R", exe);
80
1
    setenv("R_HOME", r_home, 1);
81
1
}
decompress.c:fuzz_set_r_home
Line
Count
Source
61
2
{
62
    /* Skip if R_HOME is already set (e.g., for local testing) */
63
2
    if (getenv("R_HOME") != NULL)
64
1
        return;
65
66
1
    char exe[PATH_MAX];
67
1
    ssize_t len = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
68
1
    if (len <= 0)
69
0
        return;
70
1
    exe[len] = '\0';
71
72
    /* Find the directory containing the binary */
73
1
    char *slash = strrchr(exe, '/');
74
1
    if (slash == NULL)
75
0
        return;
76
1
    *slash = '\0';
77
78
1
    char r_home[PATH_MAX];
79
1
    snprintf(r_home, sizeof(r_home), "%s/r-install/lib/R", exe);
80
1
    setenv("R_HOME", r_home, 1);
81
1
}
format.c:fuzz_set_r_home
Line
Count
Source
61
2
{
62
    /* Skip if R_HOME is already set (e.g., for local testing) */
63
2
    if (getenv("R_HOME") != NULL)
64
1
        return;
65
66
1
    char exe[PATH_MAX];
67
1
    ssize_t len = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
68
1
    if (len <= 0)
69
0
        return;
70
1
    exe[len] = '\0';
71
72
    /* Find the directory containing the binary */
73
1
    char *slash = strrchr(exe, '/');
74
1
    if (slash == NULL)
75
0
        return;
76
1
    *slash = '\0';
77
78
1
    char r_home[PATH_MAX];
79
1
    snprintf(r_home, sizeof(r_home), "%s/r-install/lib/R", exe);
80
1
    setenv("R_HOME", r_home, 1);
81
1
}
datetime.c:fuzz_set_r_home
Line
Count
Source
61
2
{
62
    /* Skip if R_HOME is already set (e.g., for local testing) */
63
2
    if (getenv("R_HOME") != NULL)
64
1
        return;
65
66
1
    char exe[PATH_MAX];
67
1
    ssize_t len = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
68
1
    if (len <= 0)
69
0
        return;
70
1
    exe[len] = '\0';
71
72
    /* Find the directory containing the binary */
73
1
    char *slash = strrchr(exe, '/');
74
1
    if (slash == NULL)
75
0
        return;
76
1
    *slash = '\0';
77
78
1
    char r_home[PATH_MAX];
79
1
    snprintf(r_home, sizeof(r_home), "%s/r-install/lib/R", exe);
80
1
    setenv("R_HOME", r_home, 1);
81
1
}
coerce.c:fuzz_set_r_home
Line
Count
Source
61
2
{
62
    /* Skip if R_HOME is already set (e.g., for local testing) */
63
2
    if (getenv("R_HOME") != NULL)
64
1
        return;
65
66
1
    char exe[PATH_MAX];
67
1
    ssize_t len = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
68
1
    if (len <= 0)
69
0
        return;
70
1
    exe[len] = '\0';
71
72
    /* Find the directory containing the binary */
73
1
    char *slash = strrchr(exe, '/');
74
1
    if (slash == NULL)
75
0
        return;
76
1
    *slash = '\0';
77
78
1
    char r_home[PATH_MAX];
79
1
    snprintf(r_home, sizeof(r_home), "%s/r-install/lib/R", exe);
80
1
    setenv("R_HOME", r_home, 1);
81
1
}
load.c:fuzz_set_r_home
Line
Count
Source
61
2
{
62
    /* Skip if R_HOME is already set (e.g., for local testing) */
63
2
    if (getenv("R_HOME") != NULL)
64
1
        return;
65
66
1
    char exe[PATH_MAX];
67
1
    ssize_t len = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
68
1
    if (len <= 0)
69
0
        return;
70
1
    exe[len] = '\0';
71
72
    /* Find the directory containing the binary */
73
1
    char *slash = strrchr(exe, '/');
74
1
    if (slash == NULL)
75
0
        return;
76
1
    *slash = '\0';
77
78
1
    char r_home[PATH_MAX];
79
1
    snprintf(r_home, sizeof(r_home), "%s/r-install/lib/R", exe);
80
1
    setenv("R_HOME", r_home, 1);
81
1
}
scan.c:fuzz_set_r_home
Line
Count
Source
61
2
{
62
    /* Skip if R_HOME is already set (e.g., for local testing) */
63
2
    if (getenv("R_HOME") != NULL)
64
1
        return;
65
66
1
    char exe[PATH_MAX];
67
1
    ssize_t len = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
68
1
    if (len <= 0)
69
0
        return;
70
1
    exe[len] = '\0';
71
72
    /* Find the directory containing the binary */
73
1
    char *slash = strrchr(exe, '/');
74
1
    if (slash == NULL)
75
0
        return;
76
1
    *slash = '\0';
77
78
1
    char r_home[PATH_MAX];
79
1
    snprintf(r_home, sizeof(r_home), "%s/r-install/lib/R", exe);
80
1
    setenv("R_HOME", r_home, 1);
81
1
}
parse.c:fuzz_set_r_home
Line
Count
Source
61
2
{
62
    /* Skip if R_HOME is already set (e.g., for local testing) */
63
2
    if (getenv("R_HOME") != NULL)
64
1
        return;
65
66
1
    char exe[PATH_MAX];
67
1
    ssize_t len = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
68
1
    if (len <= 0)
69
0
        return;
70
1
    exe[len] = '\0';
71
72
    /* Find the directory containing the binary */
73
1
    char *slash = strrchr(exe, '/');
74
1
    if (slash == NULL)
75
0
        return;
76
1
    *slash = '\0';
77
78
1
    char r_home[PATH_MAX];
79
1
    snprintf(r_home, sizeof(r_home), "%s/r-install/lib/R", exe);
80
1
    setenv("R_HOME", r_home, 1);
81
1
}
sprintf.c:fuzz_set_r_home
Line
Count
Source
61
2
{
62
    /* Skip if R_HOME is already set (e.g., for local testing) */
63
2
    if (getenv("R_HOME") != NULL)
64
1
        return;
65
66
1
    char exe[PATH_MAX];
67
1
    ssize_t len = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
68
1
    if (len <= 0)
69
0
        return;
70
1
    exe[len] = '\0';
71
72
    /* Find the directory containing the binary */
73
1
    char *slash = strrchr(exe, '/');
74
1
    if (slash == NULL)
75
0
        return;
76
1
    *slash = '\0';
77
78
1
    char r_home[PATH_MAX];
79
1
    snprintf(r_home, sizeof(r_home), "%s/r-install/lib/R", exe);
80
1
    setenv("R_HOME", r_home, 1);
81
1
}
zip.c:fuzz_set_r_home
Line
Count
Source
61
2
{
62
    /* Skip if R_HOME is already set (e.g., for local testing) */
63
2
    if (getenv("R_HOME") != NULL)
64
1
        return;
65
66
1
    char exe[PATH_MAX];
67
1
    ssize_t len = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
68
1
    if (len <= 0)
69
0
        return;
70
1
    exe[len] = '\0';
71
72
    /* Find the directory containing the binary */
73
1
    char *slash = strrchr(exe, '/');
74
1
    if (slash == NULL)
75
0
        return;
76
1
    *slash = '\0';
77
78
1
    char r_home[PATH_MAX];
79
1
    snprintf(r_home, sizeof(r_home), "%s/r-install/lib/R", exe);
80
1
    setenv("R_HOME", r_home, 1);
81
1
}
82
83
/*
84
 * Cap R's vector heap.
85
 *
86
 * Hostile input reaches R's allocator directly -- a serialized vector can
87
 * declare any length it likes, and coercion or scanning can be talked into
88
 * asking for one -- so a 64KB input can request gigabytes.
89
 *
90
 * R enforces R_MAX_VSIZE in allocVector and signals an ordinary R error
91
 * ("vector memory limit of N reached").  When it fires inside R_ToplevelExec
92
 * the iteration is discarded and fuzzing carries on; every per-iteration
93
 * allocation must therefore go through fuzz_set_string / fuzz_set_raw_arg
94
 * below, which run under such a context.  That catchability is the reason
95
 * to prefer this over a sanitizer-level RSS limit: ASAN's soft_rss_limit_mb
96
 * either aborts the process or starts handing NULL back to allocators that
97
 * never expected it, and both look like crashes.
98
 *
99
 * Caveats:
100
 *   - The cap bounds R's cumulative LIVE vector heap, not one input's
101
 *     allocations.  Interned symbols are never collected, so the parse and
102
 *     unserialize targets ratchet toward the cap over a long run.
103
 *   - It bounds R's vector heap ONLY.  malloc-based allocations, like TRE
104
 *     compiling a pattern, bypass it entirely; see
105
 *     fuzz_repeat_product_excessive for that guard.
106
 *   - It does cost coverage of code paths that need larger results.
107
 *     Targets with legitimately large outputs (decompress: a 64KB bzip2
108
 *     stream of zeros expands past 1GB) raise the default by defining
109
 *     FUZZ_R_MAX_VSIZE before including this header.
110
 *   - R's suffix parsing is case-sensitive: "4Gb" works, "4gb" silently
111
 *     leaves the heap uncapped (R only warns on stderr at startup).  The
112
 *     env override is for local runs; the CI runner does not forward
113
 *     arbitrary environment variables.
114
 *
115
 * Local reproduction of vector-limit findings under plain Rscript needs
116
 * the same value exported; see README.md.
117
 */
118
#ifndef FUZZ_R_MAX_VSIZE
119
14
# define FUZZ_R_MAX_VSIZE "1Gb"
120
#endif
121
122
static void fuzz_set_vsize_limit(void)
123
32
{
124
32
    if (getenv("R_MAX_VSIZE") == NULL)
125
16
        setenv("R_MAX_VSIZE", FUZZ_R_MAX_VSIZE, 1);
126
32
}
io.c:fuzz_set_vsize_limit
Line
Count
Source
123
2
{
124
2
    if (getenv("R_MAX_VSIZE") == NULL)
125
1
        setenv("R_MAX_VSIZE", FUZZ_R_MAX_VSIZE, 1);
126
2
}
dcf.c:fuzz_set_vsize_limit
Line
Count
Source
123
2
{
124
2
    if (getenv("R_MAX_VSIZE") == NULL)
125
1
        setenv("R_MAX_VSIZE", FUZZ_R_MAX_VSIZE, 1);
126
2
}
character.c:fuzz_set_vsize_limit
Line
Count
Source
123
2
{
124
2
    if (getenv("R_MAX_VSIZE") == NULL)
125
1
        setenv("R_MAX_VSIZE", FUZZ_R_MAX_VSIZE, 1);
126
2
}
encoding.c:fuzz_set_vsize_limit
Line
Count
Source
123
2
{
124
2
    if (getenv("R_MAX_VSIZE") == NULL)
125
1
        setenv("R_MAX_VSIZE", FUZZ_R_MAX_VSIZE, 1);
126
2
}
sort.c:fuzz_set_vsize_limit
Line
Count
Source
123
2
{
124
2
    if (getenv("R_MAX_VSIZE") == NULL)
125
1
        setenv("R_MAX_VSIZE", FUZZ_R_MAX_VSIZE, 1);
126
2
}
connections.c:fuzz_set_vsize_limit
Line
Count
Source
123
2
{
124
2
    if (getenv("R_MAX_VSIZE") == NULL)
125
1
        setenv("R_MAX_VSIZE", FUZZ_R_MAX_VSIZE, 1);
126
2
}
unserialize.c:fuzz_set_vsize_limit
Line
Count
Source
123
2
{
124
2
    if (getenv("R_MAX_VSIZE") == NULL)
125
1
        setenv("R_MAX_VSIZE", FUZZ_R_MAX_VSIZE, 1);
126
2
}
decompress.c:fuzz_set_vsize_limit
Line
Count
Source
123
2
{
124
2
    if (getenv("R_MAX_VSIZE") == NULL)
125
1
        setenv("R_MAX_VSIZE", FUZZ_R_MAX_VSIZE, 1);
126
2
}
format.c:fuzz_set_vsize_limit
Line
Count
Source
123
2
{
124
2
    if (getenv("R_MAX_VSIZE") == NULL)
125
1
        setenv("R_MAX_VSIZE", FUZZ_R_MAX_VSIZE, 1);
126
2
}
datetime.c:fuzz_set_vsize_limit
Line
Count
Source
123
2
{
124
2
    if (getenv("R_MAX_VSIZE") == NULL)
125
1
        setenv("R_MAX_VSIZE", FUZZ_R_MAX_VSIZE, 1);
126
2
}
coerce.c:fuzz_set_vsize_limit
Line
Count
Source
123
2
{
124
2
    if (getenv("R_MAX_VSIZE") == NULL)
125
1
        setenv("R_MAX_VSIZE", FUZZ_R_MAX_VSIZE, 1);
126
2
}
load.c:fuzz_set_vsize_limit
Line
Count
Source
123
2
{
124
2
    if (getenv("R_MAX_VSIZE") == NULL)
125
1
        setenv("R_MAX_VSIZE", FUZZ_R_MAX_VSIZE, 1);
126
2
}
scan.c:fuzz_set_vsize_limit
Line
Count
Source
123
2
{
124
2
    if (getenv("R_MAX_VSIZE") == NULL)
125
1
        setenv("R_MAX_VSIZE", FUZZ_R_MAX_VSIZE, 1);
126
2
}
parse.c:fuzz_set_vsize_limit
Line
Count
Source
123
2
{
124
2
    if (getenv("R_MAX_VSIZE") == NULL)
125
1
        setenv("R_MAX_VSIZE", FUZZ_R_MAX_VSIZE, 1);
126
2
}
sprintf.c:fuzz_set_vsize_limit
Line
Count
Source
123
2
{
124
2
    if (getenv("R_MAX_VSIZE") == NULL)
125
1
        setenv("R_MAX_VSIZE", FUZZ_R_MAX_VSIZE, 1);
126
2
}
zip.c:fuzz_set_vsize_limit
Line
Count
Source
123
2
{
124
2
    if (getenv("R_MAX_VSIZE") == NULL)
125
1
        setenv("R_MAX_VSIZE", FUZZ_R_MAX_VSIZE, 1);
126
2
}
127
128
/*
129
 * Initialize R for fuzzing.  Call once from LLVMFuzzerInitialize.
130
 *
131
 * Performs:
132
 *   1. Set R_HOME from binary location
133
 *   2. Cap the vector heap (must precede R's startup, which reads it)
134
 *   3. Initialize embedded R, without R's signal handlers
135
 *   4. Suppress warnings
136
 */
137
static void fuzz_init_r(void)
138
32
{
139
32
    fuzz_set_r_home();
140
32
    fuzz_set_vsize_limit();
141
142
    /* R's SIGSEGV/SIGILL/etc. handlers enter an interactive recovery
143
     * prompt instead of crashing, which would hang the fuzzer.  Telling
144
     * R not to install them (rather than resetting to SIG_DFL after
145
     * init) keeps the sanitizers' own handlers in place, so wild-pointer
146
     * faults still get full ASAN reports and clean deduplication. */
147
32
    R_SignalHandlers = 0;
148
149
32
    char *r_argv[] = {"R", "--vanilla", "--no-echo", "--no-restore"};
150
32
    int r_argc = sizeof(r_argv) / sizeof(r_argv[0]);
151
32
    Rf_initEmbeddedR(r_argc, r_argv);
152
153
32
    fuzz_suppress_warnings();
154
32
}
io.c:fuzz_init_r
Line
Count
Source
138
2
{
139
2
    fuzz_set_r_home();
140
2
    fuzz_set_vsize_limit();
141
142
    /* R's SIGSEGV/SIGILL/etc. handlers enter an interactive recovery
143
     * prompt instead of crashing, which would hang the fuzzer.  Telling
144
     * R not to install them (rather than resetting to SIG_DFL after
145
     * init) keeps the sanitizers' own handlers in place, so wild-pointer
146
     * faults still get full ASAN reports and clean deduplication. */
147
2
    R_SignalHandlers = 0;
148
149
2
    char *r_argv[] = {"R", "--vanilla", "--no-echo", "--no-restore"};
150
2
    int r_argc = sizeof(r_argv) / sizeof(r_argv[0]);
151
2
    Rf_initEmbeddedR(r_argc, r_argv);
152
153
2
    fuzz_suppress_warnings();
154
2
}
dcf.c:fuzz_init_r
Line
Count
Source
138
2
{
139
2
    fuzz_set_r_home();
140
2
    fuzz_set_vsize_limit();
141
142
    /* R's SIGSEGV/SIGILL/etc. handlers enter an interactive recovery
143
     * prompt instead of crashing, which would hang the fuzzer.  Telling
144
     * R not to install them (rather than resetting to SIG_DFL after
145
     * init) keeps the sanitizers' own handlers in place, so wild-pointer
146
     * faults still get full ASAN reports and clean deduplication. */
147
2
    R_SignalHandlers = 0;
148
149
2
    char *r_argv[] = {"R", "--vanilla", "--no-echo", "--no-restore"};
150
2
    int r_argc = sizeof(r_argv) / sizeof(r_argv[0]);
151
2
    Rf_initEmbeddedR(r_argc, r_argv);
152
153
2
    fuzz_suppress_warnings();
154
2
}
character.c:fuzz_init_r
Line
Count
Source
138
2
{
139
2
    fuzz_set_r_home();
140
2
    fuzz_set_vsize_limit();
141
142
    /* R's SIGSEGV/SIGILL/etc. handlers enter an interactive recovery
143
     * prompt instead of crashing, which would hang the fuzzer.  Telling
144
     * R not to install them (rather than resetting to SIG_DFL after
145
     * init) keeps the sanitizers' own handlers in place, so wild-pointer
146
     * faults still get full ASAN reports and clean deduplication. */
147
2
    R_SignalHandlers = 0;
148
149
2
    char *r_argv[] = {"R", "--vanilla", "--no-echo", "--no-restore"};
150
2
    int r_argc = sizeof(r_argv) / sizeof(r_argv[0]);
151
2
    Rf_initEmbeddedR(r_argc, r_argv);
152
153
2
    fuzz_suppress_warnings();
154
2
}
encoding.c:fuzz_init_r
Line
Count
Source
138
2
{
139
2
    fuzz_set_r_home();
140
2
    fuzz_set_vsize_limit();
141
142
    /* R's SIGSEGV/SIGILL/etc. handlers enter an interactive recovery
143
     * prompt instead of crashing, which would hang the fuzzer.  Telling
144
     * R not to install them (rather than resetting to SIG_DFL after
145
     * init) keeps the sanitizers' own handlers in place, so wild-pointer
146
     * faults still get full ASAN reports and clean deduplication. */
147
2
    R_SignalHandlers = 0;
148
149
2
    char *r_argv[] = {"R", "--vanilla", "--no-echo", "--no-restore"};
150
2
    int r_argc = sizeof(r_argv) / sizeof(r_argv[0]);
151
2
    Rf_initEmbeddedR(r_argc, r_argv);
152
153
2
    fuzz_suppress_warnings();
154
2
}
sort.c:fuzz_init_r
Line
Count
Source
138
2
{
139
2
    fuzz_set_r_home();
140
2
    fuzz_set_vsize_limit();
141
142
    /* R's SIGSEGV/SIGILL/etc. handlers enter an interactive recovery
143
     * prompt instead of crashing, which would hang the fuzzer.  Telling
144
     * R not to install them (rather than resetting to SIG_DFL after
145
     * init) keeps the sanitizers' own handlers in place, so wild-pointer
146
     * faults still get full ASAN reports and clean deduplication. */
147
2
    R_SignalHandlers = 0;
148
149
2
    char *r_argv[] = {"R", "--vanilla", "--no-echo", "--no-restore"};
150
2
    int r_argc = sizeof(r_argv) / sizeof(r_argv[0]);
151
2
    Rf_initEmbeddedR(r_argc, r_argv);
152
153
2
    fuzz_suppress_warnings();
154
2
}
connections.c:fuzz_init_r
Line
Count
Source
138
2
{
139
2
    fuzz_set_r_home();
140
2
    fuzz_set_vsize_limit();
141
142
    /* R's SIGSEGV/SIGILL/etc. handlers enter an interactive recovery
143
     * prompt instead of crashing, which would hang the fuzzer.  Telling
144
     * R not to install them (rather than resetting to SIG_DFL after
145
     * init) keeps the sanitizers' own handlers in place, so wild-pointer
146
     * faults still get full ASAN reports and clean deduplication. */
147
2
    R_SignalHandlers = 0;
148
149
2
    char *r_argv[] = {"R", "--vanilla", "--no-echo", "--no-restore"};
150
2
    int r_argc = sizeof(r_argv) / sizeof(r_argv[0]);
151
2
    Rf_initEmbeddedR(r_argc, r_argv);
152
153
2
    fuzz_suppress_warnings();
154
2
}
unserialize.c:fuzz_init_r
Line
Count
Source
138
2
{
139
2
    fuzz_set_r_home();
140
2
    fuzz_set_vsize_limit();
141
142
    /* R's SIGSEGV/SIGILL/etc. handlers enter an interactive recovery
143
     * prompt instead of crashing, which would hang the fuzzer.  Telling
144
     * R not to install them (rather than resetting to SIG_DFL after
145
     * init) keeps the sanitizers' own handlers in place, so wild-pointer
146
     * faults still get full ASAN reports and clean deduplication. */
147
2
    R_SignalHandlers = 0;
148
149
2
    char *r_argv[] = {"R", "--vanilla", "--no-echo", "--no-restore"};
150
2
    int r_argc = sizeof(r_argv) / sizeof(r_argv[0]);
151
2
    Rf_initEmbeddedR(r_argc, r_argv);
152
153
2
    fuzz_suppress_warnings();
154
2
}
decompress.c:fuzz_init_r
Line
Count
Source
138
2
{
139
2
    fuzz_set_r_home();
140
2
    fuzz_set_vsize_limit();
141
142
    /* R's SIGSEGV/SIGILL/etc. handlers enter an interactive recovery
143
     * prompt instead of crashing, which would hang the fuzzer.  Telling
144
     * R not to install them (rather than resetting to SIG_DFL after
145
     * init) keeps the sanitizers' own handlers in place, so wild-pointer
146
     * faults still get full ASAN reports and clean deduplication. */
147
2
    R_SignalHandlers = 0;
148
149
2
    char *r_argv[] = {"R", "--vanilla", "--no-echo", "--no-restore"};
150
2
    int r_argc = sizeof(r_argv) / sizeof(r_argv[0]);
151
2
    Rf_initEmbeddedR(r_argc, r_argv);
152
153
2
    fuzz_suppress_warnings();
154
2
}
format.c:fuzz_init_r
Line
Count
Source
138
2
{
139
2
    fuzz_set_r_home();
140
2
    fuzz_set_vsize_limit();
141
142
    /* R's SIGSEGV/SIGILL/etc. handlers enter an interactive recovery
143
     * prompt instead of crashing, which would hang the fuzzer.  Telling
144
     * R not to install them (rather than resetting to SIG_DFL after
145
     * init) keeps the sanitizers' own handlers in place, so wild-pointer
146
     * faults still get full ASAN reports and clean deduplication. */
147
2
    R_SignalHandlers = 0;
148
149
2
    char *r_argv[] = {"R", "--vanilla", "--no-echo", "--no-restore"};
150
2
    int r_argc = sizeof(r_argv) / sizeof(r_argv[0]);
151
2
    Rf_initEmbeddedR(r_argc, r_argv);
152
153
2
    fuzz_suppress_warnings();
154
2
}
datetime.c:fuzz_init_r
Line
Count
Source
138
2
{
139
2
    fuzz_set_r_home();
140
2
    fuzz_set_vsize_limit();
141
142
    /* R's SIGSEGV/SIGILL/etc. handlers enter an interactive recovery
143
     * prompt instead of crashing, which would hang the fuzzer.  Telling
144
     * R not to install them (rather than resetting to SIG_DFL after
145
     * init) keeps the sanitizers' own handlers in place, so wild-pointer
146
     * faults still get full ASAN reports and clean deduplication. */
147
2
    R_SignalHandlers = 0;
148
149
2
    char *r_argv[] = {"R", "--vanilla", "--no-echo", "--no-restore"};
150
2
    int r_argc = sizeof(r_argv) / sizeof(r_argv[0]);
151
2
    Rf_initEmbeddedR(r_argc, r_argv);
152
153
2
    fuzz_suppress_warnings();
154
2
}
coerce.c:fuzz_init_r
Line
Count
Source
138
2
{
139
2
    fuzz_set_r_home();
140
2
    fuzz_set_vsize_limit();
141
142
    /* R's SIGSEGV/SIGILL/etc. handlers enter an interactive recovery
143
     * prompt instead of crashing, which would hang the fuzzer.  Telling
144
     * R not to install them (rather than resetting to SIG_DFL after
145
     * init) keeps the sanitizers' own handlers in place, so wild-pointer
146
     * faults still get full ASAN reports and clean deduplication. */
147
2
    R_SignalHandlers = 0;
148
149
2
    char *r_argv[] = {"R", "--vanilla", "--no-echo", "--no-restore"};
150
2
    int r_argc = sizeof(r_argv) / sizeof(r_argv[0]);
151
2
    Rf_initEmbeddedR(r_argc, r_argv);
152
153
2
    fuzz_suppress_warnings();
154
2
}
load.c:fuzz_init_r
Line
Count
Source
138
2
{
139
2
    fuzz_set_r_home();
140
2
    fuzz_set_vsize_limit();
141
142
    /* R's SIGSEGV/SIGILL/etc. handlers enter an interactive recovery
143
     * prompt instead of crashing, which would hang the fuzzer.  Telling
144
     * R not to install them (rather than resetting to SIG_DFL after
145
     * init) keeps the sanitizers' own handlers in place, so wild-pointer
146
     * faults still get full ASAN reports and clean deduplication. */
147
2
    R_SignalHandlers = 0;
148
149
2
    char *r_argv[] = {"R", "--vanilla", "--no-echo", "--no-restore"};
150
2
    int r_argc = sizeof(r_argv) / sizeof(r_argv[0]);
151
2
    Rf_initEmbeddedR(r_argc, r_argv);
152
153
2
    fuzz_suppress_warnings();
154
2
}
scan.c:fuzz_init_r
Line
Count
Source
138
2
{
139
2
    fuzz_set_r_home();
140
2
    fuzz_set_vsize_limit();
141
142
    /* R's SIGSEGV/SIGILL/etc. handlers enter an interactive recovery
143
     * prompt instead of crashing, which would hang the fuzzer.  Telling
144
     * R not to install them (rather than resetting to SIG_DFL after
145
     * init) keeps the sanitizers' own handlers in place, so wild-pointer
146
     * faults still get full ASAN reports and clean deduplication. */
147
2
    R_SignalHandlers = 0;
148
149
2
    char *r_argv[] = {"R", "--vanilla", "--no-echo", "--no-restore"};
150
2
    int r_argc = sizeof(r_argv) / sizeof(r_argv[0]);
151
2
    Rf_initEmbeddedR(r_argc, r_argv);
152
153
2
    fuzz_suppress_warnings();
154
2
}
parse.c:fuzz_init_r
Line
Count
Source
138
2
{
139
2
    fuzz_set_r_home();
140
2
    fuzz_set_vsize_limit();
141
142
    /* R's SIGSEGV/SIGILL/etc. handlers enter an interactive recovery
143
     * prompt instead of crashing, which would hang the fuzzer.  Telling
144
     * R not to install them (rather than resetting to SIG_DFL after
145
     * init) keeps the sanitizers' own handlers in place, so wild-pointer
146
     * faults still get full ASAN reports and clean deduplication. */
147
2
    R_SignalHandlers = 0;
148
149
2
    char *r_argv[] = {"R", "--vanilla", "--no-echo", "--no-restore"};
150
2
    int r_argc = sizeof(r_argv) / sizeof(r_argv[0]);
151
2
    Rf_initEmbeddedR(r_argc, r_argv);
152
153
2
    fuzz_suppress_warnings();
154
2
}
sprintf.c:fuzz_init_r
Line
Count
Source
138
2
{
139
2
    fuzz_set_r_home();
140
2
    fuzz_set_vsize_limit();
141
142
    /* R's SIGSEGV/SIGILL/etc. handlers enter an interactive recovery
143
     * prompt instead of crashing, which would hang the fuzzer.  Telling
144
     * R not to install them (rather than resetting to SIG_DFL after
145
     * init) keeps the sanitizers' own handlers in place, so wild-pointer
146
     * faults still get full ASAN reports and clean deduplication. */
147
2
    R_SignalHandlers = 0;
148
149
2
    char *r_argv[] = {"R", "--vanilla", "--no-echo", "--no-restore"};
150
2
    int r_argc = sizeof(r_argv) / sizeof(r_argv[0]);
151
2
    Rf_initEmbeddedR(r_argc, r_argv);
152
153
2
    fuzz_suppress_warnings();
154
2
}
zip.c:fuzz_init_r
Line
Count
Source
138
2
{
139
2
    fuzz_set_r_home();
140
2
    fuzz_set_vsize_limit();
141
142
    /* R's SIGSEGV/SIGILL/etc. handlers enter an interactive recovery
143
     * prompt instead of crashing, which would hang the fuzzer.  Telling
144
     * R not to install them (rather than resetting to SIG_DFL after
145
     * init) keeps the sanitizers' own handlers in place, so wild-pointer
146
     * faults still get full ASAN reports and clean deduplication. */
147
2
    R_SignalHandlers = 0;
148
149
2
    char *r_argv[] = {"R", "--vanilla", "--no-echo", "--no-restore"};
150
2
    int r_argc = sizeof(r_argv) / sizeof(r_argv[0]);
151
2
    Rf_initEmbeddedR(r_argc, r_argv);
152
153
2
    fuzz_suppress_warnings();
154
2
}
155
156
/*
157
 * Helper for protected evaluation via R_ToplevelExec.
158
 *
159
 * R API calls that touch R objects can longjmp on error.  Without a
160
 * top-level context, R's error handler crashes.  Wrapping calls in
161
 * R_ToplevelExec catches the longjmp cleanly.
162
 */
163
typedef struct {
164
    SEXP call;
165
    SEXP env;
166
} fuzz_eval_data_t;
167
168
static void fuzz_do_eval(void *data)
169
12.6k
{
170
12.6k
    fuzz_eval_data_t *ed = (fuzz_eval_data_t *)data;
171
12.6k
    Rf_eval(ed->call, ed->env);
172
12.6k
}
Unexecuted instantiation: io.c:fuzz_do_eval
Unexecuted instantiation: dcf.c:fuzz_do_eval
Unexecuted instantiation: character.c:fuzz_do_eval
Unexecuted instantiation: encoding.c:fuzz_do_eval
Unexecuted instantiation: sort.c:fuzz_do_eval
Unexecuted instantiation: connections.c:fuzz_do_eval
Unexecuted instantiation: unserialize.c:fuzz_do_eval
decompress.c:fuzz_do_eval
Line
Count
Source
169
1.63k
{
170
1.63k
    fuzz_eval_data_t *ed = (fuzz_eval_data_t *)data;
171
1.63k
    Rf_eval(ed->call, ed->env);
172
1.63k
}
Unexecuted instantiation: format.c:fuzz_do_eval
datetime.c:fuzz_do_eval
Line
Count
Source
169
3.68k
{
170
3.68k
    fuzz_eval_data_t *ed = (fuzz_eval_data_t *)data;
171
3.68k
    Rf_eval(ed->call, ed->env);
172
3.68k
}
coerce.c:fuzz_do_eval
Line
Count
Source
169
7.38k
{
170
7.38k
    fuzz_eval_data_t *ed = (fuzz_eval_data_t *)data;
171
7.38k
    Rf_eval(ed->call, ed->env);
172
7.38k
}
Unexecuted instantiation: load.c:fuzz_do_eval
Unexecuted instantiation: scan.c:fuzz_do_eval
Unexecuted instantiation: parse.c:fuzz_do_eval
Unexecuted instantiation: sprintf.c:fuzz_do_eval
Unexecuted instantiation: zip.c:fuzz_do_eval
173
174
/* Evaluate malformed-input-heavy targets without printing each expected
175
 * R error. R_tryEvalSilent installs its own top-level error context. */
176
static void fuzz_eval_silent(SEXP call, SEXP env)
177
43.3k
{
178
43.3k
    int error = 0;
179
43.3k
    R_tryEvalSilent(call, env, &error);
180
43.3k
}
io.c:fuzz_eval_silent
Line
Count
Source
177
1.15k
{
178
1.15k
    int error = 0;
179
1.15k
    R_tryEvalSilent(call, env, &error);
180
1.15k
}
dcf.c:fuzz_eval_silent
Line
Count
Source
177
4.36k
{
178
4.36k
    int error = 0;
179
4.36k
    R_tryEvalSilent(call, env, &error);
180
4.36k
}
character.c:fuzz_eval_silent
Line
Count
Source
177
2.85k
{
178
2.85k
    int error = 0;
179
2.85k
    R_tryEvalSilent(call, env, &error);
180
2.85k
}
encoding.c:fuzz_eval_silent
Line
Count
Source
177
1.59k
{
178
1.59k
    int error = 0;
179
1.59k
    R_tryEvalSilent(call, env, &error);
180
1.59k
}
sort.c:fuzz_eval_silent
Line
Count
Source
177
6.96k
{
178
6.96k
    int error = 0;
179
6.96k
    R_tryEvalSilent(call, env, &error);
180
6.96k
}
connections.c:fuzz_eval_silent
Line
Count
Source
177
2.51k
{
178
2.51k
    int error = 0;
179
2.51k
    R_tryEvalSilent(call, env, &error);
180
2.51k
}
unserialize.c:fuzz_eval_silent
Line
Count
Source
177
5.28k
{
178
5.28k
    int error = 0;
179
5.28k
    R_tryEvalSilent(call, env, &error);
180
5.28k
}
Unexecuted instantiation: decompress.c:fuzz_eval_silent
format.c:fuzz_eval_silent
Line
Count
Source
177
5.26k
{
178
5.26k
    int error = 0;
179
5.26k
    R_tryEvalSilent(call, env, &error);
180
5.26k
}
datetime.c:fuzz_eval_silent
Line
Count
Source
177
1.75k
{
178
1.75k
    int error = 0;
179
1.75k
    R_tryEvalSilent(call, env, &error);
180
1.75k
}
Unexecuted instantiation: coerce.c:fuzz_eval_silent
load.c:fuzz_eval_silent
Line
Count
Source
177
332
{
178
332
    int error = 0;
179
332
    R_tryEvalSilent(call, env, &error);
180
332
}
scan.c:fuzz_eval_silent
Line
Count
Source
177
2.76k
{
178
2.76k
    int error = 0;
179
2.76k
    R_tryEvalSilent(call, env, &error);
180
2.76k
}
parse.c:fuzz_eval_silent
Line
Count
Source
177
4.65k
{
178
4.65k
    int error = 0;
179
4.65k
    R_tryEvalSilent(call, env, &error);
180
4.65k
}
sprintf.c:fuzz_eval_silent
Line
Count
Source
177
1.98k
{
178
1.98k
    int error = 0;
179
1.98k
    R_tryEvalSilent(call, env, &error);
180
1.98k
}
zip.c:fuzz_eval_silent
Line
Count
Source
177
1.89k
{
178
1.89k
    int error = 0;
179
1.89k
    R_tryEvalSilent(call, env, &error);
180
1.89k
}
181
182
/*
183
 * Stage per-iteration inputs under a toplevel context.
184
 *
185
 * Rf_mkChar and Rf_allocVector can themselves signal the vector-limit
186
 * error.  Outside a toplevel context that error longjmps into a stack
187
 * frame that returned during Rf_initEmbeddedR -- undefined behaviour --
188
 * so input staging must be wrapped just like evaluation.  Both helpers
189
 * return FALSE when the allocation failed; skip the iteration then.
190
 */
191
typedef struct {
192
    SEXP vec;
193
    const char *str;
194
} fuzz_string_data_t;
195
196
static void fuzz_do_set_string(void *data)
197
32.0k
{
198
32.0k
    fuzz_string_data_t *sd = (fuzz_string_data_t *)data;
199
32.0k
    SET_STRING_ELT(sd->vec, 0, Rf_mkChar(sd->str));
200
32.0k
}
Unexecuted instantiation: io.c:fuzz_do_set_string
dcf.c:fuzz_do_set_string
Line
Count
Source
197
4.36k
{
198
4.36k
    fuzz_string_data_t *sd = (fuzz_string_data_t *)data;
199
4.36k
    SET_STRING_ELT(sd->vec, 0, Rf_mkChar(sd->str));
200
4.36k
}
character.c:fuzz_do_set_string
Line
Count
Source
197
2.24k
{
198
2.24k
    fuzz_string_data_t *sd = (fuzz_string_data_t *)data;
199
2.24k
    SET_STRING_ELT(sd->vec, 0, Rf_mkChar(sd->str));
200
2.24k
}
encoding.c:fuzz_do_set_string
Line
Count
Source
197
733
{
198
733
    fuzz_string_data_t *sd = (fuzz_string_data_t *)data;
199
733
    SET_STRING_ELT(sd->vec, 0, Rf_mkChar(sd->str));
200
733
}
Unexecuted instantiation: sort.c:fuzz_do_set_string
Unexecuted instantiation: connections.c:fuzz_do_set_string
Unexecuted instantiation: unserialize.c:fuzz_do_set_string
Unexecuted instantiation: decompress.c:fuzz_do_set_string
format.c:fuzz_do_set_string
Line
Count
Source
197
5.26k
{
198
5.26k
    fuzz_string_data_t *sd = (fuzz_string_data_t *)data;
199
5.26k
    SET_STRING_ELT(sd->vec, 0, Rf_mkChar(sd->str));
200
5.26k
}
datetime.c:fuzz_do_set_string
Line
Count
Source
197
7.36k
{
198
7.36k
    fuzz_string_data_t *sd = (fuzz_string_data_t *)data;
199
7.36k
    SET_STRING_ELT(sd->vec, 0, Rf_mkChar(sd->str));
200
7.36k
}
coerce.c:fuzz_do_set_string
Line
Count
Source
197
1.84k
{
198
1.84k
    fuzz_string_data_t *sd = (fuzz_string_data_t *)data;
199
1.84k
    SET_STRING_ELT(sd->vec, 0, Rf_mkChar(sd->str));
200
1.84k
}
Unexecuted instantiation: load.c:fuzz_do_set_string
scan.c:fuzz_do_set_string
Line
Count
Source
197
2.72k
{
198
2.72k
    fuzz_string_data_t *sd = (fuzz_string_data_t *)data;
199
2.72k
    SET_STRING_ELT(sd->vec, 0, Rf_mkChar(sd->str));
200
2.72k
}
parse.c:fuzz_do_set_string
Line
Count
Source
197
5.53k
{
198
5.53k
    fuzz_string_data_t *sd = (fuzz_string_data_t *)data;
199
5.53k
    SET_STRING_ELT(sd->vec, 0, Rf_mkChar(sd->str));
200
5.53k
}
sprintf.c:fuzz_do_set_string
Line
Count
Source
197
1.98k
{
198
1.98k
    fuzz_string_data_t *sd = (fuzz_string_data_t *)data;
199
1.98k
    SET_STRING_ELT(sd->vec, 0, Rf_mkChar(sd->str));
200
1.98k
}
Unexecuted instantiation: zip.c:fuzz_do_set_string
201
202
static Rboolean fuzz_set_string(SEXP vec, const char *str)
203
32.0k
{
204
32.0k
    fuzz_string_data_t sd = { vec, str };
205
32.0k
    return R_ToplevelExec(fuzz_do_set_string, &sd);
206
32.0k
}
Unexecuted instantiation: io.c:fuzz_set_string
dcf.c:fuzz_set_string
Line
Count
Source
203
4.36k
{
204
4.36k
    fuzz_string_data_t sd = { vec, str };
205
4.36k
    return R_ToplevelExec(fuzz_do_set_string, &sd);
206
4.36k
}
character.c:fuzz_set_string
Line
Count
Source
203
2.24k
{
204
2.24k
    fuzz_string_data_t sd = { vec, str };
205
2.24k
    return R_ToplevelExec(fuzz_do_set_string, &sd);
206
2.24k
}
encoding.c:fuzz_set_string
Line
Count
Source
203
733
{
204
733
    fuzz_string_data_t sd = { vec, str };
205
733
    return R_ToplevelExec(fuzz_do_set_string, &sd);
206
733
}
Unexecuted instantiation: sort.c:fuzz_set_string
Unexecuted instantiation: connections.c:fuzz_set_string
Unexecuted instantiation: unserialize.c:fuzz_set_string
Unexecuted instantiation: decompress.c:fuzz_set_string
format.c:fuzz_set_string
Line
Count
Source
203
5.26k
{
204
5.26k
    fuzz_string_data_t sd = { vec, str };
205
5.26k
    return R_ToplevelExec(fuzz_do_set_string, &sd);
206
5.26k
}
datetime.c:fuzz_set_string
Line
Count
Source
203
7.36k
{
204
7.36k
    fuzz_string_data_t sd = { vec, str };
205
7.36k
    return R_ToplevelExec(fuzz_do_set_string, &sd);
206
7.36k
}
coerce.c:fuzz_set_string
Line
Count
Source
203
1.84k
{
204
1.84k
    fuzz_string_data_t sd = { vec, str };
205
1.84k
    return R_ToplevelExec(fuzz_do_set_string, &sd);
206
1.84k
}
Unexecuted instantiation: load.c:fuzz_set_string
scan.c:fuzz_set_string
Line
Count
Source
203
2.72k
{
204
2.72k
    fuzz_string_data_t sd = { vec, str };
205
2.72k
    return R_ToplevelExec(fuzz_do_set_string, &sd);
206
2.72k
}
parse.c:fuzz_set_string
Line
Count
Source
203
5.53k
{
204
5.53k
    fuzz_string_data_t sd = { vec, str };
205
5.53k
    return R_ToplevelExec(fuzz_do_set_string, &sd);
206
5.53k
}
sprintf.c:fuzz_set_string
Line
Count
Source
203
1.98k
{
204
1.98k
    fuzz_string_data_t sd = { vec, str };
205
1.98k
    return R_ToplevelExec(fuzz_do_set_string, &sd);
206
1.98k
}
Unexecuted instantiation: zip.c:fuzz_set_string
207
208
/*
209
 * Parse and evaluate one R function literal, once, at initialization.
210
 *
211
 * Harnesses whose calls need named arguments, on.exit(close(con)) or a
212
 * few R-level steps around the entry point express each slot as an R
213
 * closure and call it with Rf_lang*; that keeps the C side free of
214
 * argument plumbing.  The source is harness-authored (never fuzz
215
 * input), so a parse or evaluation failure is a harness bug: abort.
216
 */
217
static SEXP fuzz_make_wrapper(const char *source)
218
202
{
219
202
    ParseStatus status;
220
202
    SEXP text, parsed, wrapper;
221
202
    Rf_protect(text = Rf_mkString(source));
222
202
    Rf_protect(parsed = R_ParseVector(text, -1, &status, R_NilValue));
223
202
    if (status != PARSE_OK || XLENGTH(parsed) != 1)
224
0
        abort();
225
226
202
    wrapper = Rf_eval(VECTOR_ELT(parsed, 0), R_BaseEnv);
227
202
    Rf_unprotect(2);
228
202
    return wrapper;
229
202
}
io.c:fuzz_make_wrapper
Line
Count
Source
218
20
{
219
20
    ParseStatus status;
220
20
    SEXP text, parsed, wrapper;
221
20
    Rf_protect(text = Rf_mkString(source));
222
20
    Rf_protect(parsed = R_ParseVector(text, -1, &status, R_NilValue));
223
20
    if (status != PARSE_OK || XLENGTH(parsed) != 1)
224
0
        abort();
225
226
20
    wrapper = Rf_eval(VECTOR_ELT(parsed, 0), R_BaseEnv);
227
20
    Rf_unprotect(2);
228
20
    return wrapper;
229
20
}
Unexecuted instantiation: dcf.c:fuzz_make_wrapper
Unexecuted instantiation: character.c:fuzz_make_wrapper
Unexecuted instantiation: encoding.c:fuzz_make_wrapper
sort.c:fuzz_make_wrapper
Line
Count
Source
218
44
{
219
44
    ParseStatus status;
220
44
    SEXP text, parsed, wrapper;
221
44
    Rf_protect(text = Rf_mkString(source));
222
44
    Rf_protect(parsed = R_ParseVector(text, -1, &status, R_NilValue));
223
44
    if (status != PARSE_OK || XLENGTH(parsed) != 1)
224
0
        abort();
225
226
44
    wrapper = Rf_eval(VECTOR_ELT(parsed, 0), R_BaseEnv);
227
44
    Rf_unprotect(2);
228
44
    return wrapper;
229
44
}
connections.c:fuzz_make_wrapper
Line
Count
Source
218
20
{
219
20
    ParseStatus status;
220
20
    SEXP text, parsed, wrapper;
221
20
    Rf_protect(text = Rf_mkString(source));
222
20
    Rf_protect(parsed = R_ParseVector(text, -1, &status, R_NilValue));
223
20
    if (status != PARSE_OK || XLENGTH(parsed) != 1)
224
0
        abort();
225
226
20
    wrapper = Rf_eval(VECTOR_ELT(parsed, 0), R_BaseEnv);
227
20
    Rf_unprotect(2);
228
20
    return wrapper;
229
20
}
unserialize.c:fuzz_make_wrapper
Line
Count
Source
218
8
{
219
8
    ParseStatus status;
220
8
    SEXP text, parsed, wrapper;
221
8
    Rf_protect(text = Rf_mkString(source));
222
8
    Rf_protect(parsed = R_ParseVector(text, -1, &status, R_NilValue));
223
8
    if (status != PARSE_OK || XLENGTH(parsed) != 1)
224
0
        abort();
225
226
8
    wrapper = Rf_eval(VECTOR_ELT(parsed, 0), R_BaseEnv);
227
8
    Rf_unprotect(2);
228
8
    return wrapper;
229
8
}
Unexecuted instantiation: decompress.c:fuzz_make_wrapper
format.c:fuzz_make_wrapper
Line
Count
Source
218
48
{
219
48
    ParseStatus status;
220
48
    SEXP text, parsed, wrapper;
221
48
    Rf_protect(text = Rf_mkString(source));
222
48
    Rf_protect(parsed = R_ParseVector(text, -1, &status, R_NilValue));
223
48
    if (status != PARSE_OK || XLENGTH(parsed) != 1)
224
0
        abort();
225
226
48
    wrapper = Rf_eval(VECTOR_ELT(parsed, 0), R_BaseEnv);
227
48
    Rf_unprotect(2);
228
48
    return wrapper;
229
48
}
datetime.c:fuzz_make_wrapper
Line
Count
Source
218
2
{
219
2
    ParseStatus status;
220
2
    SEXP text, parsed, wrapper;
221
2
    Rf_protect(text = Rf_mkString(source));
222
2
    Rf_protect(parsed = R_ParseVector(text, -1, &status, R_NilValue));
223
2
    if (status != PARSE_OK || XLENGTH(parsed) != 1)
224
0
        abort();
225
226
2
    wrapper = Rf_eval(VECTOR_ELT(parsed, 0), R_BaseEnv);
227
2
    Rf_unprotect(2);
228
2
    return wrapper;
229
2
}
Unexecuted instantiation: coerce.c:fuzz_make_wrapper
load.c:fuzz_make_wrapper
Line
Count
Source
218
4
{
219
4
    ParseStatus status;
220
4
    SEXP text, parsed, wrapper;
221
4
    Rf_protect(text = Rf_mkString(source));
222
4
    Rf_protect(parsed = R_ParseVector(text, -1, &status, R_NilValue));
223
4
    if (status != PARSE_OK || XLENGTH(parsed) != 1)
224
0
        abort();
225
226
4
    wrapper = Rf_eval(VECTOR_ELT(parsed, 0), R_BaseEnv);
227
4
    Rf_unprotect(2);
228
4
    return wrapper;
229
4
}
scan.c:fuzz_make_wrapper
Line
Count
Source
218
36
{
219
36
    ParseStatus status;
220
36
    SEXP text, parsed, wrapper;
221
36
    Rf_protect(text = Rf_mkString(source));
222
36
    Rf_protect(parsed = R_ParseVector(text, -1, &status, R_NilValue));
223
36
    if (status != PARSE_OK || XLENGTH(parsed) != 1)
224
0
        abort();
225
226
36
    wrapper = Rf_eval(VECTOR_ELT(parsed, 0), R_BaseEnv);
227
36
    Rf_unprotect(2);
228
36
    return wrapper;
229
36
}
parse.c:fuzz_make_wrapper
Line
Count
Source
218
12
{
219
12
    ParseStatus status;
220
12
    SEXP text, parsed, wrapper;
221
12
    Rf_protect(text = Rf_mkString(source));
222
12
    Rf_protect(parsed = R_ParseVector(text, -1, &status, R_NilValue));
223
12
    if (status != PARSE_OK || XLENGTH(parsed) != 1)
224
0
        abort();
225
226
12
    wrapper = Rf_eval(VECTOR_ELT(parsed, 0), R_BaseEnv);
227
12
    Rf_unprotect(2);
228
12
    return wrapper;
229
12
}
Unexecuted instantiation: sprintf.c:fuzz_make_wrapper
zip.c:fuzz_make_wrapper
Line
Count
Source
218
8
{
219
8
    ParseStatus status;
220
8
    SEXP text, parsed, wrapper;
221
8
    Rf_protect(text = Rf_mkString(source));
222
8
    Rf_protect(parsed = R_ParseVector(text, -1, &status, R_NilValue));
223
8
    if (status != PARSE_OK || XLENGTH(parsed) != 1)
224
0
        abort();
225
226
8
    wrapper = Rf_eval(VECTOR_ELT(parsed, 0), R_BaseEnv);
227
8
    Rf_unprotect(2);
228
8
    return wrapper;
229
8
}
230
231
/*
232
 * fuzz_set_string with an explicit encoding mark.
233
 *
234
 * R does not validate the mark (Encoding<- lets users set it on any
235
 * bytes), so handing the same payload over as UTF-8 or latin1 reaches
236
 * the multibyte branches and their handling of invalid sequences
237
 * without touching the process locale.
238
 */
239
typedef struct {
240
    SEXP vec;
241
    const char *str;
242
    cetype_t ce;
243
} fuzz_ce_string_data_t;
244
245
static void fuzz_do_set_string_ce(void *data)
246
1.65k
{
247
1.65k
    fuzz_ce_string_data_t *sd = (fuzz_ce_string_data_t *)data;
248
1.65k
    SET_STRING_ELT(sd->vec, 0, Rf_mkCharCE(sd->str, sd->ce));
249
1.65k
}
Unexecuted instantiation: io.c:fuzz_do_set_string_ce
Unexecuted instantiation: dcf.c:fuzz_do_set_string_ce
character.c:fuzz_do_set_string_ce
Line
Count
Source
246
1.48k
{
247
1.48k
    fuzz_ce_string_data_t *sd = (fuzz_ce_string_data_t *)data;
248
1.48k
    SET_STRING_ELT(sd->vec, 0, Rf_mkCharCE(sd->str, sd->ce));
249
1.48k
}
encoding.c:fuzz_do_set_string_ce
Line
Count
Source
246
166
{
247
166
    fuzz_ce_string_data_t *sd = (fuzz_ce_string_data_t *)data;
248
166
    SET_STRING_ELT(sd->vec, 0, Rf_mkCharCE(sd->str, sd->ce));
249
166
}
Unexecuted instantiation: sort.c:fuzz_do_set_string_ce
Unexecuted instantiation: connections.c:fuzz_do_set_string_ce
Unexecuted instantiation: unserialize.c:fuzz_do_set_string_ce
Unexecuted instantiation: decompress.c:fuzz_do_set_string_ce
Unexecuted instantiation: format.c:fuzz_do_set_string_ce
Unexecuted instantiation: datetime.c:fuzz_do_set_string_ce
Unexecuted instantiation: coerce.c:fuzz_do_set_string_ce
Unexecuted instantiation: load.c:fuzz_do_set_string_ce
Unexecuted instantiation: scan.c:fuzz_do_set_string_ce
Unexecuted instantiation: parse.c:fuzz_do_set_string_ce
Unexecuted instantiation: sprintf.c:fuzz_do_set_string_ce
Unexecuted instantiation: zip.c:fuzz_do_set_string_ce
250
251
static Rboolean fuzz_set_string_ce(SEXP vec, const char *str, cetype_t ce)
252
1.65k
{
253
1.65k
    fuzz_ce_string_data_t sd = { vec, str, ce };
254
1.65k
    return R_ToplevelExec(fuzz_do_set_string_ce, &sd);
255
1.65k
}
Unexecuted instantiation: io.c:fuzz_set_string_ce
Unexecuted instantiation: dcf.c:fuzz_set_string_ce
character.c:fuzz_set_string_ce
Line
Count
Source
252
1.48k
{
253
1.48k
    fuzz_ce_string_data_t sd = { vec, str, ce };
254
1.48k
    return R_ToplevelExec(fuzz_do_set_string_ce, &sd);
255
1.48k
}
encoding.c:fuzz_set_string_ce
Line
Count
Source
252
166
{
253
166
    fuzz_ce_string_data_t sd = { vec, str, ce };
254
166
    return R_ToplevelExec(fuzz_do_set_string_ce, &sd);
255
166
}
Unexecuted instantiation: sort.c:fuzz_set_string_ce
Unexecuted instantiation: connections.c:fuzz_set_string_ce
Unexecuted instantiation: unserialize.c:fuzz_set_string_ce
Unexecuted instantiation: decompress.c:fuzz_set_string_ce
Unexecuted instantiation: format.c:fuzz_set_string_ce
Unexecuted instantiation: datetime.c:fuzz_set_string_ce
Unexecuted instantiation: coerce.c:fuzz_set_string_ce
Unexecuted instantiation: load.c:fuzz_set_string_ce
Unexecuted instantiation: scan.c:fuzz_set_string_ce
Unexecuted instantiation: parse.c:fuzz_set_string_ce
Unexecuted instantiation: sprintf.c:fuzz_set_string_ce
Unexecuted instantiation: zip.c:fuzz_set_string_ce
256
257
typedef struct {
258
    SEXP call;
259
    const uint8_t *data;
260
    size_t size;
261
} fuzz_raw_data_t;
262
263
static void fuzz_do_set_raw(void *data)
264
10.5k
{
265
10.5k
    fuzz_raw_data_t *rd = (fuzz_raw_data_t *)data;
266
267
    /* The raw vector is reachable from the (protected) call as soon as
268
     * SETCADR runs, and nothing allocates before the memcpy completes,
269
     * so no extra protection is needed. */
270
10.5k
    SEXP raw = Rf_allocVector(RAWSXP, (R_xlen_t)rd->size);
271
10.5k
    SETCADR(rd->call, raw);
272
10.5k
    memcpy(RAW(raw), rd->data, rd->size);
273
10.5k
}
io.c:fuzz_do_set_raw
Line
Count
Source
264
1.15k
{
265
1.15k
    fuzz_raw_data_t *rd = (fuzz_raw_data_t *)data;
266
267
    /* The raw vector is reachable from the (protected) call as soon as
268
     * SETCADR runs, and nothing allocates before the memcpy completes,
269
     * so no extra protection is needed. */
270
1.15k
    SEXP raw = Rf_allocVector(RAWSXP, (R_xlen_t)rd->size);
271
1.15k
    SETCADR(rd->call, raw);
272
1.15k
    memcpy(RAW(raw), rd->data, rd->size);
273
1.15k
}
Unexecuted instantiation: dcf.c:fuzz_do_set_raw
Unexecuted instantiation: character.c:fuzz_do_set_raw
Unexecuted instantiation: encoding.c:fuzz_do_set_raw
Unexecuted instantiation: sort.c:fuzz_do_set_raw
connections.c:fuzz_do_set_raw
Line
Count
Source
264
2.51k
{
265
2.51k
    fuzz_raw_data_t *rd = (fuzz_raw_data_t *)data;
266
267
    /* The raw vector is reachable from the (protected) call as soon as
268
     * SETCADR runs, and nothing allocates before the memcpy completes,
269
     * so no extra protection is needed. */
270
2.51k
    SEXP raw = Rf_allocVector(RAWSXP, (R_xlen_t)rd->size);
271
2.51k
    SETCADR(rd->call, raw);
272
2.51k
    memcpy(RAW(raw), rd->data, rd->size);
273
2.51k
}
unserialize.c:fuzz_do_set_raw
Line
Count
Source
264
5.27k
{
265
5.27k
    fuzz_raw_data_t *rd = (fuzz_raw_data_t *)data;
266
267
    /* The raw vector is reachable from the (protected) call as soon as
268
     * SETCADR runs, and nothing allocates before the memcpy completes,
269
     * so no extra protection is needed. */
270
5.27k
    SEXP raw = Rf_allocVector(RAWSXP, (R_xlen_t)rd->size);
271
5.27k
    SETCADR(rd->call, raw);
272
5.27k
    memcpy(RAW(raw), rd->data, rd->size);
273
5.27k
}
decompress.c:fuzz_do_set_raw
Line
Count
Source
264
1.63k
{
265
1.63k
    fuzz_raw_data_t *rd = (fuzz_raw_data_t *)data;
266
267
    /* The raw vector is reachable from the (protected) call as soon as
268
     * SETCADR runs, and nothing allocates before the memcpy completes,
269
     * so no extra protection is needed. */
270
1.63k
    SEXP raw = Rf_allocVector(RAWSXP, (R_xlen_t)rd->size);
271
1.63k
    SETCADR(rd->call, raw);
272
1.63k
    memcpy(RAW(raw), rd->data, rd->size);
273
1.63k
}
Unexecuted instantiation: format.c:fuzz_do_set_raw
Unexecuted instantiation: datetime.c:fuzz_do_set_raw
Unexecuted instantiation: coerce.c:fuzz_do_set_raw
Unexecuted instantiation: load.c:fuzz_do_set_raw
Unexecuted instantiation: scan.c:fuzz_do_set_raw
Unexecuted instantiation: parse.c:fuzz_do_set_raw
Unexecuted instantiation: sprintf.c:fuzz_do_set_raw
Unexecuted instantiation: zip.c:fuzz_do_set_raw
274
275
static Rboolean fuzz_set_raw_arg(SEXP call, const uint8_t *data, size_t size)
276
10.5k
{
277
10.5k
    fuzz_raw_data_t rd = { call, data, size };
278
10.5k
    return R_ToplevelExec(fuzz_do_set_raw, &rd);
279
10.5k
}
io.c:fuzz_set_raw_arg
Line
Count
Source
276
1.15k
{
277
1.15k
    fuzz_raw_data_t rd = { call, data, size };
278
1.15k
    return R_ToplevelExec(fuzz_do_set_raw, &rd);
279
1.15k
}
Unexecuted instantiation: dcf.c:fuzz_set_raw_arg
Unexecuted instantiation: character.c:fuzz_set_raw_arg
Unexecuted instantiation: encoding.c:fuzz_set_raw_arg
Unexecuted instantiation: sort.c:fuzz_set_raw_arg
connections.c:fuzz_set_raw_arg
Line
Count
Source
276
2.51k
{
277
2.51k
    fuzz_raw_data_t rd = { call, data, size };
278
2.51k
    return R_ToplevelExec(fuzz_do_set_raw, &rd);
279
2.51k
}
unserialize.c:fuzz_set_raw_arg
Line
Count
Source
276
5.27k
{
277
5.27k
    fuzz_raw_data_t rd = { call, data, size };
278
5.27k
    return R_ToplevelExec(fuzz_do_set_raw, &rd);
279
5.27k
}
decompress.c:fuzz_set_raw_arg
Line
Count
Source
276
1.63k
{
277
1.63k
    fuzz_raw_data_t rd = { call, data, size };
278
1.63k
    return R_ToplevelExec(fuzz_do_set_raw, &rd);
279
1.63k
}
Unexecuted instantiation: format.c:fuzz_set_raw_arg
Unexecuted instantiation: datetime.c:fuzz_set_raw_arg
Unexecuted instantiation: coerce.c:fuzz_set_raw_arg
Unexecuted instantiation: load.c:fuzz_set_raw_arg
Unexecuted instantiation: scan.c:fuzz_set_raw_arg
Unexecuted instantiation: parse.c:fuzz_set_raw_arg
Unexecuted instantiation: sprintf.c:fuzz_set_raw_arg
Unexecuted instantiation: zip.c:fuzz_set_raw_arg
280
281
/*
282
 * Stage the input in a scratch file.
283
 *
284
 * Some R entry points only take a path (load() of a pre-1.4 save file,
285
 * unzip(), file(encoding=)), so those targets rewrite one per-process
286
 * file each iteration instead of handing R a raw vector.  The file lives
287
 * in the system temp directory, which OSS-Fuzz runners allow writes to,
288
 * and is created once in LLVMFuzzerInitialize so fuzzing never pays a
289
 * mkstemp per input.  Returns a malloc'd path, or NULL on failure.
290
 */
291
static char *fuzz_scratch_file(const char *name)
292
6
{
293
6
    const char *tmpdir = getenv("TMPDIR");
294
6
    if (tmpdir == NULL || *tmpdir == '\0')
295
6
        tmpdir = "/tmp";
296
297
6
    char *path = malloc(PATH_MAX);
298
6
    if (path == NULL)
299
0
        return NULL;
300
6
    snprintf(path, PATH_MAX, "%s/fuzz-%s-XXXXXX", tmpdir, name);
301
302
6
    int fd = mkstemp(path);
303
6
    if (fd < 0) {
304
0
        free(path);
305
0
        return NULL;
306
0
    }
307
6
    close(fd);
308
6
    return path;
309
6
}
Unexecuted instantiation: io.c:fuzz_scratch_file
Unexecuted instantiation: dcf.c:fuzz_scratch_file
Unexecuted instantiation: character.c:fuzz_scratch_file
Unexecuted instantiation: encoding.c:fuzz_scratch_file
Unexecuted instantiation: sort.c:fuzz_scratch_file
connections.c:fuzz_scratch_file
Line
Count
Source
292
2
{
293
2
    const char *tmpdir = getenv("TMPDIR");
294
2
    if (tmpdir == NULL || *tmpdir == '\0')
295
2
        tmpdir = "/tmp";
296
297
2
    char *path = malloc(PATH_MAX);
298
2
    if (path == NULL)
299
0
        return NULL;
300
2
    snprintf(path, PATH_MAX, "%s/fuzz-%s-XXXXXX", tmpdir, name);
301
302
2
    int fd = mkstemp(path);
303
2
    if (fd < 0) {
304
0
        free(path);
305
0
        return NULL;
306
0
    }
307
2
    close(fd);
308
2
    return path;
309
2
}
Unexecuted instantiation: unserialize.c:fuzz_scratch_file
Unexecuted instantiation: decompress.c:fuzz_scratch_file
Unexecuted instantiation: format.c:fuzz_scratch_file
Unexecuted instantiation: datetime.c:fuzz_scratch_file
Unexecuted instantiation: coerce.c:fuzz_scratch_file
load.c:fuzz_scratch_file
Line
Count
Source
292
2
{
293
2
    const char *tmpdir = getenv("TMPDIR");
294
2
    if (tmpdir == NULL || *tmpdir == '\0')
295
2
        tmpdir = "/tmp";
296
297
2
    char *path = malloc(PATH_MAX);
298
2
    if (path == NULL)
299
0
        return NULL;
300
2
    snprintf(path, PATH_MAX, "%s/fuzz-%s-XXXXXX", tmpdir, name);
301
302
2
    int fd = mkstemp(path);
303
2
    if (fd < 0) {
304
0
        free(path);
305
0
        return NULL;
306
0
    }
307
2
    close(fd);
308
2
    return path;
309
2
}
Unexecuted instantiation: scan.c:fuzz_scratch_file
Unexecuted instantiation: parse.c:fuzz_scratch_file
Unexecuted instantiation: sprintf.c:fuzz_scratch_file
zip.c:fuzz_scratch_file
Line
Count
Source
292
2
{
293
2
    const char *tmpdir = getenv("TMPDIR");
294
2
    if (tmpdir == NULL || *tmpdir == '\0')
295
2
        tmpdir = "/tmp";
296
297
2
    char *path = malloc(PATH_MAX);
298
2
    if (path == NULL)
299
0
        return NULL;
300
2
    snprintf(path, PATH_MAX, "%s/fuzz-%s-XXXXXX", tmpdir, name);
301
302
2
    int fd = mkstemp(path);
303
2
    if (fd < 0) {
304
0
        free(path);
305
0
        return NULL;
306
0
    }
307
2
    close(fd);
308
2
    return path;
309
2
}
310
311
/* Replace the scratch file's contents.  Returns FALSE on any I/O error so
312
 * the iteration can be skipped rather than fuzzing a stale file. */
313
static Rboolean fuzz_write_scratch(const char *path, const uint8_t *data,
314
                                   size_t size)
315
4.74k
{
316
4.74k
    FILE *fp = fopen(path, "wb");
317
4.74k
    if (fp == NULL)
318
0
        return FALSE;
319
320
4.74k
    size_t written = size > 0 ? fwrite(data, 1, size, fp) : 0;
321
4.74k
    int rc = fclose(fp);
322
4.74k
    return written == size && rc == 0;
323
4.74k
}
Unexecuted instantiation: io.c:fuzz_write_scratch
Unexecuted instantiation: dcf.c:fuzz_write_scratch
Unexecuted instantiation: character.c:fuzz_write_scratch
Unexecuted instantiation: encoding.c:fuzz_write_scratch
Unexecuted instantiation: sort.c:fuzz_write_scratch
connections.c:fuzz_write_scratch
Line
Count
Source
315
2.51k
{
316
2.51k
    FILE *fp = fopen(path, "wb");
317
2.51k
    if (fp == NULL)
318
0
        return FALSE;
319
320
2.51k
    size_t written = size > 0 ? fwrite(data, 1, size, fp) : 0;
321
2.51k
    int rc = fclose(fp);
322
2.51k
    return written == size && rc == 0;
323
2.51k
}
Unexecuted instantiation: unserialize.c:fuzz_write_scratch
Unexecuted instantiation: decompress.c:fuzz_write_scratch
Unexecuted instantiation: format.c:fuzz_write_scratch
Unexecuted instantiation: datetime.c:fuzz_write_scratch
Unexecuted instantiation: coerce.c:fuzz_write_scratch
load.c:fuzz_write_scratch
Line
Count
Source
315
332
{
316
332
    FILE *fp = fopen(path, "wb");
317
332
    if (fp == NULL)
318
0
        return FALSE;
319
320
332
    size_t written = size > 0 ? fwrite(data, 1, size, fp) : 0;
321
332
    int rc = fclose(fp);
322
332
    return written == size && rc == 0;
323
332
}
Unexecuted instantiation: scan.c:fuzz_write_scratch
Unexecuted instantiation: parse.c:fuzz_write_scratch
Unexecuted instantiation: sprintf.c:fuzz_write_scratch
zip.c:fuzz_write_scratch
Line
Count
Source
315
1.89k
{
316
1.89k
    FILE *fp = fopen(path, "wb");
317
1.89k
    if (fp == NULL)
318
0
        return FALSE;
319
320
1.89k
    size_t written = size > 0 ? fwrite(data, 1, size, fp) : 0;
321
1.89k
    int rc = fclose(fp);
322
1.89k
    return written == size && rc == 0;
323
1.89k
}
324
325
/*
326
 * Guard against TRE's bounded-repeat blowup.
327
 *
328
 * TRE compiles bounded repeats by duplicating the pattern AST, so nested
329
 * counted repeats multiply: a 36-byte pattern like
330
 * (?:a{2,101,})(?:a{2,101,}){100}{100} expands to ~10^8 nodes and >8GB of
331
 * allocations at compile time.  Those allocations are malloc, not R's
332
 * vector heap, so R_MAX_VSIZE cannot bound them; reject the pattern
333
 * before it reaches R instead.
334
 *
335
 * The product of all repeat bounds is a deliberate over-estimate --
336
 * sequential (non-nested) repeats add rather than multiply -- trading a
337
 * little coverage of repeat-heavy patterns for a hard bound on compile
338
 * cost.
339
 */
340
253
#define FUZZ_MAX_REPEAT_PRODUCT 1000000.0
341
342
static int fuzz_repeat_product_excessive(const char *pattern)
343
258
{
344
258
    double product = 1.0;
345
346
160k
    for (const char *p = pattern; *p != '\0'; p++) {
347
160k
        if (*p != '{')
348
159k
            continue;
349
350
        /* The largest number inside the braces bounds this repeat. */
351
1.58k
        unsigned long bound = 0, cur = 0;
352
1.58k
        int counted = 0;
353
1.58k
        const char *q = p + 1;
354
7.39k
        for (; *q != '\0' && *q != '}'; q++) {
355
7.13k
            if (*q >= '0' && *q <= '9') {
356
5.35k
                cur = cur * 10 + (unsigned long)(*q - '0');
357
5.35k
                if (cur > 10000000)
358
1.40k
                    cur = 10000000;  /* saturate; already over any budget */
359
5.35k
                counted = 1;
360
5.35k
            } else if (*q == ',') {
361
451
                if (cur > bound)
362
351
                    bound = cur;
363
451
                cur = 0;
364
1.32k
            } else {
365
1.32k
                counted = 0;  /* not a counted repeat, e.g. "{a}" */
366
1.32k
                break;
367
1.32k
            }
368
7.13k
        }
369
1.58k
        if (!counted || *q != '}')
370
1.32k
            continue;
371
372
253
        if (cur > bound)
373
140
            bound = cur;
374
253
        if (bound > 1)
375
160
            product *= (double)bound;
376
253
        if (product > FUZZ_MAX_REPEAT_PRODUCT)
377
9
            return 1;
378
379
244
        p = q;
380
244
    }
381
382
249
    return 0;
383
258
}
Unexecuted instantiation: io.c:fuzz_repeat_product_excessive
Unexecuted instantiation: dcf.c:fuzz_repeat_product_excessive
character.c:fuzz_repeat_product_excessive
Line
Count
Source
343
258
{
344
258
    double product = 1.0;
345
346
160k
    for (const char *p = pattern; *p != '\0'; p++) {
347
160k
        if (*p != '{')
348
159k
            continue;
349
350
        /* The largest number inside the braces bounds this repeat. */
351
1.58k
        unsigned long bound = 0, cur = 0;
352
1.58k
        int counted = 0;
353
1.58k
        const char *q = p + 1;
354
7.39k
        for (; *q != '\0' && *q != '}'; q++) {
355
7.13k
            if (*q >= '0' && *q <= '9') {
356
5.35k
                cur = cur * 10 + (unsigned long)(*q - '0');
357
5.35k
                if (cur > 10000000)
358
1.40k
                    cur = 10000000;  /* saturate; already over any budget */
359
5.35k
                counted = 1;
360
5.35k
            } else if (*q == ',') {
361
451
                if (cur > bound)
362
351
                    bound = cur;
363
451
                cur = 0;
364
1.32k
            } else {
365
1.32k
                counted = 0;  /* not a counted repeat, e.g. "{a}" */
366
1.32k
                break;
367
1.32k
            }
368
7.13k
        }
369
1.58k
        if (!counted || *q != '}')
370
1.32k
            continue;
371
372
253
        if (cur > bound)
373
140
            bound = cur;
374
253
        if (bound > 1)
375
160
            product *= (double)bound;
376
253
        if (product > FUZZ_MAX_REPEAT_PRODUCT)
377
9
            return 1;
378
379
244
        p = q;
380
244
    }
381
382
249
    return 0;
383
258
}
Unexecuted instantiation: encoding.c:fuzz_repeat_product_excessive
Unexecuted instantiation: sort.c:fuzz_repeat_product_excessive
Unexecuted instantiation: connections.c:fuzz_repeat_product_excessive
Unexecuted instantiation: unserialize.c:fuzz_repeat_product_excessive
Unexecuted instantiation: decompress.c:fuzz_repeat_product_excessive
Unexecuted instantiation: format.c:fuzz_repeat_product_excessive
Unexecuted instantiation: datetime.c:fuzz_repeat_product_excessive
Unexecuted instantiation: coerce.c:fuzz_repeat_product_excessive
Unexecuted instantiation: load.c:fuzz_repeat_product_excessive
Unexecuted instantiation: scan.c:fuzz_repeat_product_excessive
Unexecuted instantiation: parse.c:fuzz_repeat_product_excessive
Unexecuted instantiation: sprintf.c:fuzz_repeat_product_excessive
Unexecuted instantiation: zip.c:fuzz_repeat_product_excessive
384
385
#endif /* FUZZ_COMMON_H */