Coverage Report

Created: 2026-03-31 07:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fuzz_bignum.cpp
Line
Count
Source
1
/* Copyright 2025 Google LLC
2
Licensed under the Apache License, Version 2.0 (the "License");
3
you may not use this file except in compliance with the License.
4
You may obtain a copy of the License at
5
      http://www.apache.org/licenses/LICENSE-2.0
6
Unless required by applicable law or agreed to in writing, software
7
distributed under the License is distributed on an "AS IS" BASIS,
8
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9
See the License for the specific language governing permissions and
10
limitations under the License.
11
*/
12
13
/*
14
 * Fuzzer for Ruby's Bignum/Integer implementation (bignum.c, numeric.c)
15
 * 
16
 * Purpose: Test arbitrary-precision integer operations including arithmetic,
17
 * bitwise operations, conversions, and comparison. Tests edge cases in
18
 * overflow handling, sign handling, and numeric precision.
19
 * 
20
 * Coverage:
21
 * - Arithmetic: +, -, *, /, %, **
22
 * - Bitwise: &, |, ^, <<, >>
23
 * - Comparison: ==, <=>, <, >, <=, >=
24
 * - Conversion: to_s, to_f, abs, negation
25
 * - Edge cases: Division by zero, large exponents, overflow
26
 */
27
28
#include <stdint.h>
29
#include <stddef.h>
30
#include <stdlib.h>
31
#include <fuzzer/FuzzedDataProvider.h>
32
#include "ruby.h"
33
34
static int ruby_initialized = 0;
35
36
extern "C" VALUE ruby_verbose;
37
38
7.26k
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
39
7.26k
    if (!ruby_initialized) {
40
1
        ruby_init();
41
1
        ruby_initialized = 1;
42
        
43
        // Suppress Ruby warnings to avoid log noise
44
1
        ruby_verbose = Qfalse;
45
1
    }
46
    
47
7.26k
    if (size < 2) return 0;
48
    
49
    // Use FuzzedDataProvider for structured data consumption
50
7.26k
    FuzzedDataProvider fdp(data, size);
51
    
52
7.26k
    int state = 0;
53
7.26k
    VALUE num1 = Qnil, num2 = Qnil, result = Qnil;
54
    
55
    // Create first number from fuzzer data
56
7.26k
    size_t str1_len = fdp.ConsumeIntegralInRange<size_t>(0, 10000);
57
7.26k
    std::string str1 = fdp.ConsumeBytesAsString(str1_len);
58
7.26k
    num1 = rb_protect((VALUE (*)(VALUE))rb_Integer, rb_str_new(str1.data(), str1.size()), &state);
59
7.26k
    if (state) {
60
1.68k
        rb_set_errinfo(Qnil);
61
1.68k
        state = 0;
62
1.68k
        num1 = INT2FIX(fdp.ConsumeIntegral<int32_t>());
63
1.68k
    }
64
    
65
    // Create second number from remaining fuzzer data
66
7.26k
    size_t str2_len = fdp.ConsumeIntegralInRange<size_t>(0, 10000);
67
7.26k
    std::string str2 = fdp.ConsumeBytesAsString(str2_len);
68
7.26k
    if (!str2.empty()) {
69
5.20k
        num2 = rb_protect((VALUE (*)(VALUE))rb_Integer, rb_str_new(str2.data(), str2.size()), &state);
70
5.20k
        if (state) {
71
1.88k
            rb_set_errinfo(Qnil);
72
1.88k
            state = 0;
73
1.88k
            num2 = INT2FIX(fdp.ConsumeIntegral<int32_t>());
74
1.88k
        }
75
5.20k
    } else {
76
2.05k
        num2 = INT2FIX(1);
77
2.05k
    }
78
    
79
    // Select a single operation to test
80
7.26k
    uint8_t op = fdp.ConsumeIntegralInRange<uint8_t>(0, 19);
81
    
82
7.26k
    switch (op) {
83
1.66k
        case 0: // Addition
84
1.66k
            result = rb_funcall(num1, rb_intern("+"), 1, num2);
85
1.66k
            break;
86
56
        case 1: // Subtraction
87
56
            result = rb_funcall(num1, rb_intern("-"), 1, num2);
88
56
            break;
89
105
        case 2: // Multiplication
90
105
            result = rb_funcall(num1, rb_intern("*"), 1, num2);
91
105
            break;
92
438
        case 3: // Division (skip if num2 is zero)
93
438
            if (!FIXNUM_P(num2) || FIX2LONG(num2) != 0) {
94
438
                result = rb_funcall(num1, rb_intern("/"), 1, num2);
95
438
            }
96
438
            break;
97
370
        case 4: // Modulo (skip if num2 is zero)
98
370
            if (!FIXNUM_P(num2) || FIX2LONG(num2) != 0) {
99
370
                result = rb_funcall(num1, rb_intern("%"), 1, num2);
100
370
            }
101
370
            break;
102
2.01k
        case 5: // Power (limit exponent to avoid hang)
103
2.01k
            if (FIXNUM_P(num2)) {
104
2.01k
                long exp_val = FIX2LONG(num2);
105
2.01k
                if (exp_val >= 0 && exp_val < 100) {
106
1.94k
                    result = rb_funcall(num1, rb_intern("**"), 1, num2);
107
1.94k
                }
108
2.01k
            }
109
2.01k
            break;
110
338
        case 6: // Bitwise AND
111
338
            result = rb_funcall(num1, rb_intern("&"), 1, num2);
112
338
            break;
113
309
        case 7: // Bitwise OR
114
309
            result = rb_funcall(num1, rb_intern("|"), 1, num2);
115
309
            break;
116
228
        case 8: // Bitwise XOR
117
228
            result = rb_funcall(num1, rb_intern("^"), 1, num2);
118
228
            break;
119
251
        case 9: // Left shift (limit shift amount)
120
251
            if (FIXNUM_P(num2)) {
121
249
                long shift = FIX2LONG(num2);
122
249
                if (shift >= 0 && shift < 256) {
123
174
                    result = rb_funcall(num1, rb_intern("<<"), 1, num2);
124
174
                }
125
249
            }
126
251
            break;
127
272
        case 10: // Right shift (limit shift amount)
128
272
            if (FIXNUM_P(num2)) {
129
271
                long shift = FIX2LONG(num2);
130
271
                if (shift >= 0 && shift < 256) {
131
206
                    result = rb_funcall(num1, rb_intern(">>"), 1, num2);
132
206
                }
133
271
            }
134
272
            break;
135
128
        case 11: // Equality
136
128
            result = rb_funcall(num1, rb_intern("=="), 1, num2);
137
128
            break;
138
134
        case 12: // Spaceship operator (comparison)
139
134
            result = rb_funcall(num1, rb_intern("<=>"), 1, num2);
140
134
            break;
141
89
        case 13: // Greater than
142
89
            result = rb_funcall(num1, rb_intern(">"), 1, num2);
143
89
            break;
144
119
        case 14: // Less than
145
119
            result = rb_funcall(num1, rb_intern("<"), 1, num2);
146
119
            break;
147
437
        case 15: // To string conversion
148
437
            result = rb_funcall(num1, rb_intern("to_s"), 0);
149
437
            break;
150
102
        case 16: // To float conversion
151
102
            result = rb_funcall(num1, rb_intern("to_f"), 0);
152
102
            break;
153
111
        case 17: // Absolute value
154
111
            result = rb_funcall(num1, rb_intern("abs"), 0);
155
111
            break;
156
93
        case 18: // Negation
157
93
            result = rb_funcall(num1, rb_intern("-@"), 0);
158
93
            break;
159
0
        case 19: // Bitwise NOT
160
0
            result = rb_funcall(num1, rb_intern("~"), 0);
161
0
            break;
162
7.26k
    }
163
    
164
    // Force GC
165
7.26k
    rb_gc_start();
166
    
167
7.26k
    return 0;
168
7.26k
}