Coverage Report

Created: 2025-08-29 06:20

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