/src/tremor/ogg_fuzzer.cc
Line | Count | Source |
1 | | /* Copyright 2026 Google LLC |
2 | | |
3 | | Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | you may not use this file except in compliance with the License. |
5 | | You may obtain a copy of the License at |
6 | | |
7 | | http://www.apache.org/licenses/LICENSE-2.0 |
8 | | |
9 | | Unless required by applicable law or agreed to in writing, software |
10 | | distributed under the License is distributed on an "AS IS" BASIS, |
11 | | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | See the License for the specific language governing permissions and |
13 | | limitations under the License. |
14 | | */ |
15 | | |
16 | | #include <stdint.h> |
17 | | #include <string.h> |
18 | | #include <stdlib.h> |
19 | | #include <ogg/ogg.h> |
20 | | |
21 | 1.10k | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
22 | 1.10k | if (size < 10) return 0; |
23 | | |
24 | | // Use the first few bytes to decide what to do |
25 | 1.09k | uint8_t action = data[0]; |
26 | 1.09k | uint32_t serial = (data[1] << 24) | (data[2] << 16) | (data[3] << 8) | data[4]; |
27 | 1.09k | const uint8_t *fuzz_data = data + 5; |
28 | 1.09k | size_t fuzz_size = size - 5; |
29 | | |
30 | | // Fuzz ogg_sync and ogg_stream |
31 | 1.09k | ogg_sync_state oy; |
32 | 1.09k | ogg_stream_state os; |
33 | 1.09k | ogg_sync_init(&oy); |
34 | 1.09k | ogg_stream_init(&os, serial); |
35 | | |
36 | 1.09k | char *buf = ogg_sync_buffer(&oy, fuzz_size); |
37 | 1.09k | if (buf) { |
38 | 1.09k | memcpy(buf, fuzz_data, fuzz_size); |
39 | 1.09k | ogg_sync_wrote(&oy, fuzz_size); |
40 | 1.09k | } |
41 | | |
42 | 1.09k | ogg_page og; |
43 | 12.8k | while (ogg_sync_pageout(&oy, &og) == 1) { |
44 | 11.7k | ogg_stream_pagein(&os, &og); |
45 | 11.7k | ogg_packet op; |
46 | 31.1k | while (ogg_stream_packetout(&os, &op) == 1) { |
47 | | // Successfully extracted a packet |
48 | 19.3k | } |
49 | 11.7k | } |
50 | | |
51 | 1.09k | ogg_stream_clear(&os); |
52 | 1.09k | ogg_sync_clear(&oy); |
53 | | |
54 | | // Fuzz oggpack (bitwise) - LSb |
55 | 1.09k | oggpack_buffer opb; |
56 | 1.09k | oggpack_writeinit(&opb); |
57 | | |
58 | 1.09k | size_t pos = 0; |
59 | 636k | while (pos + 5 <= fuzz_size) { |
60 | 635k | uint8_t op_choice = fuzz_data[pos++]; |
61 | 635k | uint32_t val = (fuzz_data[pos] << 24) | (fuzz_data[pos+1] << 16) | (fuzz_data[pos+2] << 8) | fuzz_data[pos+3]; |
62 | 635k | pos += 4; |
63 | | |
64 | 635k | switch (op_choice % 4) { |
65 | 615k | case 0: |
66 | 615k | oggpack_write(&opb, val, fuzz_data[pos-1] % 33); |
67 | 615k | break; |
68 | 13.1k | case 1: |
69 | 13.1k | oggpack_writealign(&opb); |
70 | 13.1k | break; |
71 | 757 | case 2: |
72 | 757 | oggpack_writecopy(&opb, (void*)(fuzz_data + pos), (fuzz_size - pos) * 8); |
73 | 757 | pos = fuzz_size; // consume rest |
74 | 757 | break; |
75 | 5.43k | case 3: |
76 | 5.43k | oggpack_reset(&opb); |
77 | 5.43k | break; |
78 | 635k | } |
79 | 635k | } |
80 | | |
81 | | // Read back what we wrote |
82 | 1.09k | unsigned char *out_buf = oggpack_get_buffer(&opb); |
83 | 1.09k | long out_bytes = oggpack_bytes(&opb); |
84 | 1.09k | if (out_buf && out_bytes > 0) { |
85 | 947 | oggpack_buffer opr; |
86 | 947 | oggpack_readinit(&opr, out_buf, out_bytes); |
87 | 10.4k | for (int i = 0; i < 10; ++i) { |
88 | 9.47k | oggpack_look(&opr, 10); |
89 | 9.47k | oggpack_adv(&opr, 1); |
90 | 9.47k | oggpack_read(&opr, 8); |
91 | 9.47k | } |
92 | 947 | } |
93 | 1.09k | oggpack_writeclear(&opb); |
94 | | |
95 | | // Fuzz oggpackB (bitwise) - MSb |
96 | 1.09k | oggpackB_writeinit(&opb); |
97 | 1.09k | pos = 0; |
98 | 636k | while (pos + 5 <= fuzz_size) { |
99 | 635k | uint8_t op_choice = fuzz_data[pos++]; |
100 | 635k | uint32_t val = (fuzz_data[pos] << 24) | (fuzz_data[pos+1] << 16) | (fuzz_data[pos+2] << 8) | fuzz_data[pos+3]; |
101 | 635k | pos += 4; |
102 | | |
103 | 635k | switch (op_choice % 4) { |
104 | 615k | case 0: |
105 | 615k | oggpackB_write(&opb, val, fuzz_data[pos-1] % 33); |
106 | 615k | break; |
107 | 13.1k | case 1: |
108 | 13.1k | oggpackB_writealign(&opb); |
109 | 13.1k | break; |
110 | 757 | case 2: |
111 | 757 | oggpackB_writecopy(&opb, (void*)(fuzz_data + pos), (fuzz_size - pos) * 8); |
112 | 757 | pos = fuzz_size; // consume rest |
113 | 757 | break; |
114 | 5.43k | case 3: |
115 | 5.43k | oggpackB_reset(&opb); |
116 | 5.43k | break; |
117 | 635k | } |
118 | 635k | } |
119 | 1.09k | out_buf = oggpackB_get_buffer(&opb); |
120 | 1.09k | out_bytes = oggpackB_bytes(&opb); |
121 | 1.09k | if (out_buf && out_bytes > 0) { |
122 | 947 | oggpack_buffer opr; |
123 | 947 | oggpackB_readinit(&opr, out_buf, out_bytes); |
124 | 10.4k | for (int i = 0; i < 10; ++i) { |
125 | 9.47k | oggpackB_look(&opr, 10); |
126 | 9.47k | oggpackB_adv(&opr, 1); |
127 | 9.47k | oggpackB_read(&opr, 8); |
128 | 9.47k | } |
129 | 947 | } |
130 | 1.09k | oggpackB_writeclear(&opb); |
131 | | |
132 | 1.09k | return 0; |
133 | 1.09k | } |