Coverage Report

Created: 2025-11-11 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libcups/ossfuzz/fuzzfile.c
Line
Count
Source
1
/*
2
 * File I/O Fuzzer for libcups
3
 *
4
 * This fuzzer tests file operations including opening, reading, writing,
5
 * seeking, and file manipulation functionality in the libcups library.
6
 *
7
 * Licensed under Apache License v2.0.
8
 * See the file "LICENSE" for more information.
9
 */
10
11
#include <stdio.h>
12
#include <stdlib.h>
13
#include <string.h>
14
#include <unistd.h>
15
#include <stdint.h>
16
#include <errno.h>
17
#include "cups.h"
18
#include "file.h"
19
#include "dir.h"
20
21
// Global variables for cleanup
22
static char *g_temp_files[16];
23
static int g_temp_file_count = 0;
24
25
// Cleanup function
26
static void cleanup_files(void)
27
1.04k
{
28
4.02k
    for (int i = 0; i < g_temp_file_count; i++)
29
2.97k
    {
30
2.97k
        if (g_temp_files[i])
31
2.97k
        {
32
2.97k
            unlink(g_temp_files[i]);
33
2.97k
            free(g_temp_files[i]);
34
2.97k
            g_temp_files[i] = NULL;
35
2.97k
        }
36
2.97k
    }
37
1.04k
    g_temp_file_count = 0;
38
1.04k
}
39
40
// Add temp file to cleanup list
41
static void add_temp_file(const char *filename)
42
2.97k
{
43
2.97k
    if (g_temp_file_count < 16)
44
2.97k
    {
45
2.97k
        g_temp_files[g_temp_file_count] = strdup(filename);
46
2.97k
        g_temp_file_count++;
47
2.97k
    }
48
2.97k
}
49
50
// Parse input data into segments
51
static int parse_file_segments(const uint8_t *data, size_t size,
52
                               const uint8_t **segments, size_t *seg_sizes, int max_segments)
53
523
{
54
523
    if (size < 4)
55
2
        return 0;
56
57
521
    uint32_t num_segments = *(uint32_t *)data % max_segments + 1;
58
521
    data += 4;
59
521
    size -= 4;
60
61
521
    size_t pos = 0;
62
521
    int count = 0;
63
64
2.12k
    for (uint32_t i = 0; i < num_segments && count < max_segments && pos < size; i++)
65
1.71k
    {
66
1.71k
        if (pos + 2 >= size)
67
103
            break;
68
69
1.60k
        uint16_t seg_len = *(uint16_t *)(data + pos) % (size - pos - 2) + 1;
70
1.60k
        pos += 2;
71
72
1.60k
        if (pos + seg_len <= size)
73
1.60k
        {
74
1.60k
            segments[count] = data + pos;
75
1.60k
            seg_sizes[count] = seg_len;
76
1.60k
            count++;
77
1.60k
            pos += seg_len;
78
1.60k
        }
79
1.60k
    }
80
81
521
    return count;
82
523
}
83
84
// Test basic file operations
85
static void test_file_basic_operations(const uint8_t *data, size_t size)
86
1.60k
{
87
1.60k
    if (size == 0)
88
0
        return;
89
90
1.60k
    char filename[256];
91
1.60k
    snprintf(filename, sizeof(filename), "/tmp/fuzz_file_%d_%zu.test", getpid(), size);
92
1.60k
    add_temp_file(filename);
93
94
1.60k
    cups_file_t *fp = NULL;
95
96
    // Test cupsFileOpen for writing
97
1.60k
    fp = cupsFileOpen(filename, "w");
98
1.60k
    if (fp)
99
1.60k
    {
100
        // Test cupsFileWrite
101
1.60k
        size_t written = cupsFileWrite(fp, (char *)data, size);
102
1.60k
        (void)written;
103
104
        // Test cupsFilePrintf
105
1.60k
        cupsFilePrintf(fp, "\nTest line %zu\n", size);
106
107
        // Test cupsFilePuts
108
1.60k
        cupsFilePuts(fp, "Test string\n");
109
110
        // Test cupsFilePutChar
111
1.60k
        cupsFilePutChar(fp, 'X');
112
113
        // Test cupsFileFlush
114
1.60k
        cupsFileFlush(fp);
115
116
        // Test cupsFileClose
117
1.60k
        cupsFileClose(fp);
118
1.60k
    }
119
120
    // Test cupsFileOpen for reading
121
1.60k
    fp = cupsFileOpen(filename, "r");
122
1.60k
    if (fp)
123
1.60k
    {
124
        // Test cupsFileRead
125
1.60k
        char buffer[1024];
126
1.60k
        size_t bytes_read = cupsFileRead(fp, buffer, sizeof(buffer) - 1);
127
1.60k
        if (bytes_read > 0)
128
1.60k
        {
129
1.60k
            buffer[bytes_read] = '\0';
130
1.60k
        }
131
132
        // Test cupsFileRewind
133
1.60k
        cupsFileRewind(fp);
134
135
        // Test cupsFileGets
136
1.60k
        char line_buffer[256];
137
537k
        while (cupsFileGets(fp, line_buffer, sizeof(line_buffer)))
138
536k
        {
139
            // Process line
140
536k
        }
141
142
        // Test cupsFileRewind again
143
1.60k
        cupsFileRewind(fp);
144
145
        // Test cupsFileGetChar
146
1.60k
        int ch;
147
13.3M
        while ((ch = cupsFileGetChar(fp)) != EOF)
148
13.3M
        {
149
            // Process character
150
13.3M
        }
151
152
        // Test cupsFileTell and cupsFileSeek
153
1.60k
        off_t pos = cupsFileTell(fp);
154
1.60k
        cupsFileSeek(fp, 0);
155
1.60k
        cupsFileSeek(fp, pos);
156
157
        // Test cupsFileNumber
158
1.60k
        int fd = cupsFileNumber(fp);
159
1.60k
        (void)fd;
160
161
        // Test cupsFileIsCompressed
162
1.60k
        bool compressed = cupsFileIsCompressed(fp);
163
1.60k
        (void)compressed;
164
165
1.60k
        cupsFileClose(fp);
166
1.60k
    }
167
1.60k
}
168
169
// Test compressed file operations
170
static void test_file_compression(const uint8_t *data, size_t size)
171
434
{
172
434
    if (size == 0)
173
0
        return;
174
175
434
    char filename[256];
176
434
    snprintf(filename, sizeof(filename), "/tmp/fuzz_compressed_%d_%zu.gz", getpid(), size);
177
434
    add_temp_file(filename);
178
179
434
    cups_file_t *fp = NULL;
180
181
    // Test writing compressed file
182
434
    fp = cupsFileOpen(filename, "w9"); // 9 = maximum compression
183
434
    if (fp)
184
434
    {
185
434
        cupsFileWrite(fp, (char *)data, size);
186
434
        cupsFilePrintf(fp, "\nCompressed data test\n");
187
434
        cupsFileClose(fp);
188
189
        // Test reading compressed file
190
434
        fp = cupsFileOpen(filename, "r");
191
434
        if (fp)
192
434
        {
193
434
            char buffer[2048];
194
434
            size_t read_size = cupsFileRead(fp, buffer, sizeof(buffer) - 1);
195
434
            (void)read_size;
196
197
            // Test compressed file properties
198
434
            bool is_compressed = cupsFileIsCompressed(fp);
199
434
            (void)is_compressed;
200
201
434
            cupsFileClose(fp);
202
434
        }
203
434
    }
204
434
}
205
206
// Test file finding and path operations
207
static void test_file_path_operations(const uint8_t *data, size_t size)
208
523
{
209
523
    if (size < 16)
210
89
        return;
211
212
    // Create test file in current directory
213
434
    char test_filename[64];
214
434
    snprintf(test_filename, sizeof(test_filename), "test_find_%d.tmp", data[0] | (data[1] << 8));
215
434
    add_temp_file(test_filename);
216
217
434
    cups_file_t *fp = cupsFileOpen(test_filename, "w");
218
434
    if (fp)
219
434
    {
220
434
        cupsFileWrite(fp, "test content", 12);
221
434
        cupsFileClose(fp);
222
223
        // Test cupsFileFind
224
434
        char found_path[1024];
225
434
        if (cupsFileFind(test_filename, ".", 1, found_path, sizeof(found_path)))
226
434
        {
227
            // File found
228
434
        }
229
230
        // Test with multiple search paths
231
434
        if (cupsFileFind(test_filename, ".:/tmp", 1, found_path, sizeof(found_path)))
232
0
        {
233
            // File found in search path
234
0
        }
235
434
    }
236
434
}
237
238
// Test directory operations
239
static void test_directory_operations(const uint8_t *data, size_t size)
240
523
{
241
523
    if (size < 4)
242
2
        return;
243
244
521
    char dirname[256];
245
521
    snprintf(dirname, sizeof(dirname), "/tmp/fuzz_dir_%d_%zu", getpid(), size);
246
247
    // Create test directory
248
521
    if (mkdir(dirname, 0755) == 0)
249
521
    {
250
        // Create some test files in the directory
251
2.08k
        for (int i = 0; i < 3 && i < size; i++)
252
1.56k
        {
253
1.56k
            char filepath[512];
254
1.56k
            snprintf(filepath, sizeof(filepath), "%s/file_%d.txt", dirname, i);
255
256
1.56k
            cups_file_t *fp = cupsFileOpen(filepath, "w");
257
1.56k
            if (fp)
258
1.56k
            {
259
1.56k
                cupsFilePrintf(fp, "File %d content: %d\n", i, data[i]);
260
1.56k
                cupsFileClose(fp);
261
1.56k
            }
262
1.56k
        }
263
264
        // Test cupsDirOpen
265
521
        cups_dir_t *dir = cupsDirOpen(dirname);
266
521
        if (dir)
267
521
        {
268
            // Test cupsDirRead
269
521
            cups_dentry_t *entry;
270
2.08k
            while ((entry = cupsDirRead(dir)) != NULL)
271
1.56k
            {
272
                // Process directory entry
273
1.56k
                const char *filename = entry->filename;
274
1.56k
                (void)filename;
275
1.56k
            }
276
277
            // Test cupsDirRewind
278
521
            cupsDirRewind(dir);
279
280
            // Read again after rewind
281
521
            entry = cupsDirRead(dir);
282
521
            (void)entry;
283
284
521
            cupsDirClose(dir);
285
521
        }
286
287
        // Cleanup directory and files
288
2.08k
        for (int i = 0; i < 3 && i < size; i++)
289
1.56k
        {
290
1.56k
            char filepath[512];
291
1.56k
            snprintf(filepath, sizeof(filepath), "%s/file_%d.txt", dirname, i);
292
1.56k
            unlink(filepath);
293
1.56k
        }
294
521
        rmdir(dirname);
295
521
    }
296
521
}
297
298
// Test file locking operations
299
static void test_file_locking(const uint8_t *data, size_t size)
300
499
{
301
499
    if (size < 8)
302
0
        return;
303
304
499
    char filename[256];
305
499
    snprintf(filename, sizeof(filename), "/tmp/fuzz_lock_%d_%zu.test", getpid(), size);
306
499
    add_temp_file(filename);
307
308
499
    cups_file_t *fp = cupsFileOpen(filename, "w+");
309
499
    if (fp)
310
499
    {
311
        // Write some data
312
499
        cupsFileWrite(fp, (char *)data, size);
313
314
        // Test cupsFileLock
315
499
        int lock_result = cupsFileLock(fp, 1); // blocking lock
316
499
        (void)lock_result;
317
318
        // Test cupsFileUnlock
319
499
        int unlock_result = cupsFileUnlock(fp);
320
499
        (void)unlock_result;
321
322
        // Test non-blocking lock
323
499
        lock_result = cupsFileLock(fp, 0); // non-blocking
324
499
        if (lock_result == 0)
325
0
        {
326
0
            cupsFileUnlock(fp);
327
0
        }
328
329
499
        cupsFileClose(fp);
330
499
    }
331
499
}
332
333
extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
334
531
{
335
531
    if (size == 0 || size > 65536)
336
8
    {
337
8
        return 0;
338
8
    }
339
340
    // Setup cleanup handler
341
523
    atexit(cleanup_files);
342
343
523
    const uint8_t *segments[8];
344
523
    size_t seg_sizes[8];
345
523
    int num_segments = parse_file_segments(data, size, segments, seg_sizes, 8);
346
347
    // Test 1: Basic file operations
348
2.13k
    for (int i = 0; i < num_segments; i++)
349
1.60k
    {
350
1.60k
        test_file_basic_operations(segments[i], seg_sizes[i]);
351
1.60k
    }
352
353
    // Test 2: Compressed file operations
354
523
    if (size >= 16)
355
434
    {
356
434
        test_file_compression(data, size);
357
434
    }
358
359
    // Test 3: File path and finding operations
360
523
    test_file_path_operations(data, size);
361
362
    // Test 4: Directory operations
363
523
    test_directory_operations(data, size);
364
365
    // Test 5: File locking operations
366
523
    if (size >= 8)
367
499
    {
368
499
        test_file_locking(data, size);
369
499
    }
370
371
    // Cleanup
372
523
    cleanup_files();
373
374
523
    return 0;
375
531
}