Coverage Report

Created: 2025-11-15 06:14

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.05k
{
28
4.04k
    for (int i = 0; i < g_temp_file_count; i++)
29
2.99k
    {
30
2.99k
        if (g_temp_files[i])
31
2.99k
        {
32
2.99k
            unlink(g_temp_files[i]);
33
2.99k
            free(g_temp_files[i]);
34
2.99k
            g_temp_files[i] = NULL;
35
2.99k
        }
36
2.99k
    }
37
1.05k
    g_temp_file_count = 0;
38
1.05k
}
39
40
// Add temp file to cleanup list
41
static void add_temp_file(const char *filename)
42
2.99k
{
43
2.99k
    if (g_temp_file_count < 16)
44
2.99k
    {
45
2.99k
        g_temp_files[g_temp_file_count] = strdup(filename);
46
2.99k
        g_temp_file_count++;
47
2.99k
    }
48
2.99k
}
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
525
{
54
525
    if (size < 4)
55
2
        return 0;
56
57
523
    uint32_t num_segments = *(uint32_t *)data % max_segments + 1;
58
523
    data += 4;
59
523
    size -= 4;
60
61
523
    size_t pos = 0;
62
523
    int count = 0;
63
64
2.14k
    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
100
            break;
68
69
1.61k
        uint16_t seg_len = *(uint16_t *)(data + pos) % (size - pos - 2) + 1;
70
1.61k
        pos += 2;
71
72
1.61k
        if (pos + seg_len <= size)
73
1.61k
        {
74
1.61k
            segments[count] = data + pos;
75
1.61k
            seg_sizes[count] = seg_len;
76
1.61k
            count++;
77
1.61k
            pos += seg_len;
78
1.61k
        }
79
1.61k
    }
80
81
523
    return count;
82
525
}
83
84
// Test basic file operations
85
static void test_file_basic_operations(const uint8_t *data, size_t size)
86
1.61k
{
87
1.61k
    if (size == 0)
88
0
        return;
89
90
1.61k
    char filename[256];
91
1.61k
    snprintf(filename, sizeof(filename), "/tmp/fuzz_file_%d_%zu.test", getpid(), size);
92
1.61k
    add_temp_file(filename);
93
94
1.61k
    cups_file_t *fp = NULL;
95
96
    // Test cupsFileOpen for writing
97
1.61k
    fp = cupsFileOpen(filename, "w");
98
1.61k
    if (fp)
99
1.61k
    {
100
        // Test cupsFileWrite
101
1.61k
        size_t written = cupsFileWrite(fp, (char *)data, size);
102
1.61k
        (void)written;
103
104
        // Test cupsFilePrintf
105
1.61k
        cupsFilePrintf(fp, "\nTest line %zu\n", size);
106
107
        // Test cupsFilePuts
108
1.61k
        cupsFilePuts(fp, "Test string\n");
109
110
        // Test cupsFilePutChar
111
1.61k
        cupsFilePutChar(fp, 'X');
112
113
        // Test cupsFileFlush
114
1.61k
        cupsFileFlush(fp);
115
116
        // Test cupsFileClose
117
1.61k
        cupsFileClose(fp);
118
1.61k
    }
119
120
    // Test cupsFileOpen for reading
121
1.61k
    fp = cupsFileOpen(filename, "r");
122
1.61k
    if (fp)
123
1.61k
    {
124
        // Test cupsFileRead
125
1.61k
        char buffer[1024];
126
1.61k
        size_t bytes_read = cupsFileRead(fp, buffer, sizeof(buffer) - 1);
127
1.61k
        if (bytes_read > 0)
128
1.61k
        {
129
1.61k
            buffer[bytes_read] = '\0';
130
1.61k
        }
131
132
        // Test cupsFileRewind
133
1.61k
        cupsFileRewind(fp);
134
135
        // Test cupsFileGets
136
1.61k
        char line_buffer[256];
137
534k
        while (cupsFileGets(fp, line_buffer, sizeof(line_buffer)))
138
532k
        {
139
            // Process line
140
532k
        }
141
142
        // Test cupsFileRewind again
143
1.61k
        cupsFileRewind(fp);
144
145
        // Test cupsFileGetChar
146
1.61k
        int ch;
147
13.7M
        while ((ch = cupsFileGetChar(fp)) != EOF)
148
13.7M
        {
149
            // Process character
150
13.7M
        }
151
152
        // Test cupsFileTell and cupsFileSeek
153
1.61k
        off_t pos = cupsFileTell(fp);
154
1.61k
        cupsFileSeek(fp, 0);
155
1.61k
        cupsFileSeek(fp, pos);
156
157
        // Test cupsFileNumber
158
1.61k
        int fd = cupsFileNumber(fp);
159
1.61k
        (void)fd;
160
161
        // Test cupsFileIsCompressed
162
1.61k
        bool compressed = cupsFileIsCompressed(fp);
163
1.61k
        (void)compressed;
164
165
1.61k
        cupsFileClose(fp);
166
1.61k
    }
167
1.61k
}
168
169
// Test compressed file operations
170
static void test_file_compression(const uint8_t *data, size_t size)
171
437
{
172
437
    if (size == 0)
173
0
        return;
174
175
437
    char filename[256];
176
437
    snprintf(filename, sizeof(filename), "/tmp/fuzz_compressed_%d_%zu.gz", getpid(), size);
177
437
    add_temp_file(filename);
178
179
437
    cups_file_t *fp = NULL;
180
181
    // Test writing compressed file
182
437
    fp = cupsFileOpen(filename, "w9"); // 9 = maximum compression
183
437
    if (fp)
184
437
    {
185
437
        cupsFileWrite(fp, (char *)data, size);
186
437
        cupsFilePrintf(fp, "\nCompressed data test\n");
187
437
        cupsFileClose(fp);
188
189
        // Test reading compressed file
190
437
        fp = cupsFileOpen(filename, "r");
191
437
        if (fp)
192
437
        {
193
437
            char buffer[2048];
194
437
            size_t read_size = cupsFileRead(fp, buffer, sizeof(buffer) - 1);
195
437
            (void)read_size;
196
197
            // Test compressed file properties
198
437
            bool is_compressed = cupsFileIsCompressed(fp);
199
437
            (void)is_compressed;
200
201
437
            cupsFileClose(fp);
202
437
        }
203
437
    }
204
437
}
205
206
// Test file finding and path operations
207
static void test_file_path_operations(const uint8_t *data, size_t size)
208
525
{
209
525
    if (size < 16)
210
88
        return;
211
212
    // Create test file in current directory
213
437
    char test_filename[64];
214
437
    snprintf(test_filename, sizeof(test_filename), "test_find_%d.tmp", data[0] | (data[1] << 8));
215
437
    add_temp_file(test_filename);
216
217
437
    cups_file_t *fp = cupsFileOpen(test_filename, "w");
218
437
    if (fp)
219
437
    {
220
437
        cupsFileWrite(fp, "test content", 12);
221
437
        cupsFileClose(fp);
222
223
        // Test cupsFileFind
224
437
        char found_path[1024];
225
437
        if (cupsFileFind(test_filename, ".", 1, found_path, sizeof(found_path)))
226
437
        {
227
            // File found
228
437
        }
229
230
        // Test with multiple search paths
231
437
        if (cupsFileFind(test_filename, ".:/tmp", 1, found_path, sizeof(found_path)))
232
0
        {
233
            // File found in search path
234
0
        }
235
437
    }
236
437
}
237
238
// Test directory operations
239
static void test_directory_operations(const uint8_t *data, size_t size)
240
525
{
241
525
    if (size < 4)
242
2
        return;
243
244
523
    char dirname[256];
245
523
    snprintf(dirname, sizeof(dirname), "/tmp/fuzz_dir_%d_%zu", getpid(), size);
246
247
    // Create test directory
248
523
    if (mkdir(dirname, 0755) == 0)
249
523
    {
250
        // Create some test files in the directory
251
2.09k
        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
523
        cups_dir_t *dir = cupsDirOpen(dirname);
266
523
        if (dir)
267
523
        {
268
            // Test cupsDirRead
269
523
            cups_dentry_t *entry;
270
2.09k
            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
523
            cupsDirRewind(dir);
279
280
            // Read again after rewind
281
523
            entry = cupsDirRead(dir);
282
523
            (void)entry;
283
284
523
            cupsDirClose(dir);
285
523
        }
286
287
        // Cleanup directory and files
288
2.09k
        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
523
        rmdir(dirname);
295
523
    }
296
523
}
297
298
// Test file locking operations
299
static void test_file_locking(const uint8_t *data, size_t size)
300
503
{
301
503
    if (size < 8)
302
0
        return;
303
304
503
    char filename[256];
305
503
    snprintf(filename, sizeof(filename), "/tmp/fuzz_lock_%d_%zu.test", getpid(), size);
306
503
    add_temp_file(filename);
307
308
503
    cups_file_t *fp = cupsFileOpen(filename, "w+");
309
503
    if (fp)
310
503
    {
311
        // Write some data
312
503
        cupsFileWrite(fp, (char *)data, size);
313
314
        // Test cupsFileLock
315
503
        int lock_result = cupsFileLock(fp, 1); // blocking lock
316
503
        (void)lock_result;
317
318
        // Test cupsFileUnlock
319
503
        int unlock_result = cupsFileUnlock(fp);
320
503
        (void)unlock_result;
321
322
        // Test non-blocking lock
323
503
        lock_result = cupsFileLock(fp, 0); // non-blocking
324
503
        if (lock_result == 0)
325
0
        {
326
0
            cupsFileUnlock(fp);
327
0
        }
328
329
503
        cupsFileClose(fp);
330
503
    }
331
503
}
332
333
extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
334
533
{
335
533
    if (size == 0 || size > 65536)
336
8
    {
337
8
        return 0;
338
8
    }
339
340
    // Setup cleanup handler
341
525
    atexit(cleanup_files);
342
343
525
    const uint8_t *segments[8];
344
525
    size_t seg_sizes[8];
345
525
    int num_segments = parse_file_segments(data, size, segments, seg_sizes, 8);
346
347
    // Test 1: Basic file operations
348
2.14k
    for (int i = 0; i < num_segments; i++)
349
1.61k
    {
350
1.61k
        test_file_basic_operations(segments[i], seg_sizes[i]);
351
1.61k
    }
352
353
    // Test 2: Compressed file operations
354
525
    if (size >= 16)
355
437
    {
356
437
        test_file_compression(data, size);
357
437
    }
358
359
    // Test 3: File path and finding operations
360
525
    test_file_path_operations(data, size);
361
362
    // Test 4: Directory operations
363
525
    test_directory_operations(data, size);
364
365
    // Test 5: File locking operations
366
525
    if (size >= 8)
367
503
    {
368
503
        test_file_locking(data, size);
369
503
    }
370
371
    // Cleanup
372
525
    cleanup_files();
373
374
525
    return 0;
375
533
}