Coverage Report

Created: 2026-06-10 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cups/ossfuzz/fuzz_ppd_gen_cache.c
Line
Count
Source
1
#include <stdio.h>
2
#include <stdlib.h>
3
#include <string.h>
4
#include <unistd.h>
5
#include <stdint.h>
6
#include "ppd.h"
7
#include "ppd-private.h"
8
#include "cups.h"
9
#include "ipp.h"
10
#include "file-private.h"
11
12
// Global variables for cleanup
13
static char *g_ppd_file = NULL;
14
static char *g_cache_file = NULL;
15
16
// Cleanup function
17
static void cleanup_files(void)
18
17.3k
{
19
17.3k
    if (g_ppd_file)
20
8.68k
    {
21
8.68k
        unlink(g_ppd_file);
22
8.68k
        free(g_ppd_file);
23
8.68k
        g_ppd_file = NULL;
24
8.68k
    }
25
17.3k
    if (g_cache_file)
26
8.68k
    {
27
8.68k
        unlink(g_cache_file);
28
8.68k
        free(g_cache_file);
29
8.68k
        g_cache_file = NULL;
30
8.68k
    }
31
17.3k
}
32
33
// Parse input data into segments
34
static int parse_input_segments(const uint8_t *data, size_t size,
35
                                const uint8_t **segments, size_t *seg_sizes, int max_segments)
36
8.68k
{
37
8.68k
    if (size < 4)
38
0
        return 0;
39
40
8.68k
    uint32_t num_segments = *(uint32_t *)data;
41
8.68k
    if (num_segments == 0 || num_segments > max_segments)
42
42
        return 0;
43
44
8.64k
    const uint8_t *ptr = data + 4;
45
8.64k
    size_t remaining = size - 4;
46
47
22.2k
    for (uint32_t i = 0; i < num_segments && i < max_segments; i++)
48
19.6k
    {
49
19.6k
        if (remaining < 4)
50
3.75k
            return i;
51
52
15.9k
        uint32_t seg_len = *(uint32_t *)ptr;
53
15.9k
        ptr += 4;
54
15.9k
        remaining -= 4;
55
56
15.9k
        if (seg_len > remaining)
57
2.33k
            return i;
58
59
13.5k
        segments[i] = ptr;
60
13.5k
        seg_sizes[i] = seg_len;
61
13.5k
        ptr += seg_len;
62
13.5k
        remaining -= seg_len;
63
13.5k
    }
64
65
2.55k
    return num_segments;
66
8.64k
}
67
68
// Create a simple IPP job for testing
69
static ipp_t *create_test_job(const uint8_t *data, size_t size)
70
700
{
71
700
    ipp_t *job = ippNew();
72
700
    if (!job)
73
0
        return NULL;
74
75
    // Add some basic attributes using fuzz data
76
700
    if (size > 10)
77
444
    {
78
444
        char media_name[64];
79
444
        snprintf(media_name, sizeof(media_name), "media-%02x%02x", data[0], data[1]);
80
444
        ippAddString(job, IPP_TAG_JOB, IPP_TAG_KEYWORD, "media", NULL, media_name);
81
444
    }
82
83
700
    if (size > 20)
84
408
    {
85
408
        char source_name[64];
86
408
        snprintf(source_name, sizeof(source_name), "source-%02x%02x", data[10], data[11]);
87
408
        ippAddString(job, IPP_TAG_JOB, IPP_TAG_KEYWORD, "media-source", NULL, source_name);
88
408
    }
89
90
700
    if (size > 30)
91
220
    {
92
220
        char type_name[64];
93
220
        snprintf(type_name, sizeof(type_name), "type-%02x%02x", data[20], data[21]);
94
220
        ippAddString(job, IPP_TAG_JOB, IPP_TAG_KEYWORD, "media-type", NULL, type_name);
95
220
    }
96
97
700
    return job;
98
700
}
99
100
// Test all cache GET functions
101
static void test_cache_functions(_ppd_cache_t *pc, ppd_file_t *ppd, const uint8_t **segments, size_t *seg_sizes, int num_segments)
102
11.6k
{
103
11.6k
    if (!pc)
104
0
        return;
105
106
    // Test _ppdCacheGetBin
107
11.6k
    if (num_segments > 4 && seg_sizes[4] > 0)
108
53
    {
109
53
        char bin_name[256];
110
53
        size_t copy_len = seg_sizes[4] < sizeof(bin_name) - 1 ? seg_sizes[4] : sizeof(bin_name) - 1;
111
53
        memcpy(bin_name, segments[4], copy_len);
112
53
        bin_name[copy_len] = '\0';
113
114
53
        const char *result = _ppdCacheGetBin(pc, bin_name);
115
53
        (void)result; // Suppress unused variable warning
116
53
    }
117
118
    // Test _ppdCacheGetOutputBin
119
11.6k
    if (num_segments > 4 && seg_sizes[4] > 0)
120
53
    {
121
53
        char output_bin[256];
122
53
        size_t copy_len = seg_sizes[4] < sizeof(output_bin) - 1 ? seg_sizes[4] : sizeof(output_bin) - 1;
123
53
        memcpy(output_bin, segments[4], copy_len);
124
53
        output_bin[copy_len] = '\0';
125
126
53
        const char *result = _ppdCacheGetOutputBin(pc, output_bin);
127
53
        (void)result;
128
53
    }
129
130
    // Test _ppdCacheGetSource
131
11.6k
    if (num_segments > 3 && seg_sizes[3] > 0)
132
57
    {
133
57
        char input_slot[256];
134
57
        size_t copy_len = seg_sizes[3] < sizeof(input_slot) - 1 ? seg_sizes[3] : sizeof(input_slot) - 1;
135
57
        memcpy(input_slot, segments[3], copy_len);
136
57
        input_slot[copy_len] = '\0';
137
138
57
        const char *result = _ppdCacheGetSource(pc, input_slot);
139
57
        (void)result;
140
57
    }
141
142
    // Test _ppdCacheGetType
143
11.6k
    if (num_segments > 2 && seg_sizes[2] > 0)
144
90
    {
145
90
        char media_type[256];
146
90
        size_t copy_len = seg_sizes[2] < sizeof(media_type) - 1 ? seg_sizes[2] : sizeof(media_type) - 1;
147
90
        memcpy(media_type, segments[2], copy_len);
148
90
        media_type[copy_len] = '\0';
149
150
90
        const char *result = _ppdCacheGetType(pc, media_type);
151
90
        (void)result;
152
90
    }
153
154
    // Test _ppdCacheGetPageSize with keyword
155
11.6k
    if (num_segments > 1 && seg_sizes[1] > 0)
156
1.14k
    {
157
1.14k
        char page_size[256];
158
1.14k
        size_t copy_len = seg_sizes[1] < sizeof(page_size) - 1 ? seg_sizes[1] : sizeof(page_size) - 1;
159
1.14k
        memcpy(page_size, segments[1], copy_len);
160
1.14k
        page_size[copy_len] = '\0';
161
162
1.14k
        int exact = 0;
163
1.14k
        const char *result = _ppdCacheGetPageSize(pc, NULL, page_size, &exact);
164
1.14k
        (void)result;
165
1.14k
    }
166
167
    // Test _ppdCacheGetPageSize with IPP job
168
11.6k
    if (num_segments > 5 && seg_sizes[5] > 0)
169
175
    {
170
175
        ipp_t *job = create_test_job(segments[5], seg_sizes[5]);
171
175
        if (job)
172
175
        {
173
175
            int exact = 0;
174
175
            const char *result = _ppdCacheGetPageSize(pc, job, NULL, &exact);
175
175
            (void)result;
176
175
            ippDelete(job);
177
175
        }
178
175
    }
179
180
    // Test _ppdCacheGetInputSlot
181
11.6k
    if (num_segments > 5 && seg_sizes[5] > 0)
182
175
    {
183
175
        ipp_t *job = create_test_job(segments[5], seg_sizes[5]);
184
175
        if (job)
185
175
        {
186
175
            const char *result = _ppdCacheGetInputSlot(pc, job, NULL);
187
175
            (void)result;
188
175
            ippDelete(job);
189
175
        }
190
175
    }
191
192
    // Test _ppdCacheGetMediaType
193
11.6k
    if (num_segments > 5 && seg_sizes[5] > 0)
194
175
    {
195
175
        ipp_t *job = create_test_job(segments[5], seg_sizes[5]);
196
175
        if (job)
197
175
        {
198
175
            const char *result = _ppdCacheGetMediaType(pc, job, NULL);
199
175
            (void)result;
200
175
            ippDelete(job);
201
175
        }
202
175
    }
203
204
    // Test _ppdCacheGetSize
205
11.6k
    if (num_segments > 1 && seg_sizes[1] > 0)
206
1.14k
    {
207
1.14k
        char page_size[256];
208
1.14k
        size_t copy_len = seg_sizes[1] < sizeof(page_size) - 1 ? seg_sizes[1] : sizeof(page_size) - 1;
209
1.14k
        memcpy(page_size, segments[1], copy_len);
210
1.14k
        page_size[copy_len] = '\0';
211
212
1.14k
        pwg_size_t *result = _ppdCacheGetSize(pc, page_size, NULL);
213
1.14k
        (void)result;
214
1.14k
    }
215
216
    // Test _ppdCacheGetFinishingValues
217
11.6k
    if (ppd)
218
11.6k
    {
219
11.6k
        int finishings[20];
220
11.6k
        int num_finishings = _ppdCacheGetFinishingValues(ppd, pc, 20, finishings);
221
11.6k
        (void)num_finishings;
222
11.6k
    }
223
224
    // Test _ppdCacheGetFinishingOptions
225
11.6k
    if (num_segments > 5 && seg_sizes[5] > 0)
226
175
    {
227
175
        ipp_t *job = create_test_job(segments[5], seg_sizes[5]);
228
175
        if (job)
229
175
        {
230
175
            cups_option_t *options = NULL;
231
175
            int num_options = 0;
232
175
            num_options = _ppdCacheGetFinishingOptions(pc, job, IPP_FINISHINGS_NONE, num_options, &options);
233
234
175
            if (options)
235
0
            {
236
0
                cupsFreeOptions(num_options, options);
237
0
            }
238
175
            ippDelete(job);
239
175
        }
240
175
    }
241
11.6k
}
242
243
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
244
8.69k
{
245
    // Minimum size check
246
8.69k
    if (size < 100)
247
11
    {
248
11
        return 0;
249
11
    }
250
251
    // Setup cleanup
252
8.68k
    atexit(cleanup_files);
253
254
    // Create filenames with cursor_test_ prefix
255
8.68k
    g_ppd_file = malloc(256);
256
8.68k
    g_cache_file = malloc(256);
257
8.68k
    if (!g_ppd_file || !g_cache_file)
258
0
    {
259
0
        cleanup_files();
260
0
        return 0;
261
0
    }
262
263
8.68k
    snprintf(g_ppd_file, 256, "/tmp/cursor_test_ppd_%d.ppd", getpid());
264
8.68k
    snprintf(g_cache_file, 256, "/tmp/cursor_test_cache_%d.cache", getpid());
265
266
    // Parse input segments
267
8.68k
    const uint8_t *segments[10];
268
8.68k
    size_t seg_sizes[10];
269
8.68k
    int num_segments = parse_input_segments(data, size, segments, seg_sizes, 10);
270
271
8.68k
    if (num_segments < 1)
272
42
    {
273
42
        cleanup_files();
274
42
        return 0;
275
42
    }
276
277
    // Write PPD file data
278
8.64k
    FILE *file = fopen(g_ppd_file, "wb");
279
8.64k
    if (!file)
280
0
    {
281
0
        cleanup_files();
282
0
        return 0;
283
0
    }
284
285
8.64k
    fwrite(segments[0], 1, seg_sizes[0], file);
286
8.64k
    fclose(file);
287
288
    // Open PPD file
289
8.64k
    ppd_file_t *ppd = ppdOpenFile(g_ppd_file);
290
8.64k
    if (!ppd)
291
2.37k
    {
292
2.37k
        cleanup_files();
293
2.37k
        return 0;
294
2.37k
    }
295
296
    // Create cache from PPD
297
6.26k
    _ppd_cache_t *cache = _ppdCacheCreateWithPPD(NULL, ppd);
298
6.26k
    if (!cache)
299
0
    {
300
0
        ppdClose(ppd);
301
0
        cleanup_files();
302
0
        return 0;
303
0
    }
304
305
    // Test all cache functions
306
6.26k
    test_cache_functions(cache, ppd, segments, seg_sizes, num_segments);
307
308
    // Test cache file write/read cycle
309
6.26k
    if (_ppdCacheWriteFile(cache, g_cache_file, NULL))
310
6.26k
    {
311
6.26k
        ipp_t *attrs = NULL;
312
6.26k
        _ppd_cache_t *cache2 = _ppdCacheCreateWithFile(g_cache_file, &attrs);
313
6.26k
        if (cache2)
314
5.35k
        {
315
            // Test functions on loaded cache
316
5.35k
            test_cache_functions(cache2, ppd, segments, seg_sizes, num_segments);
317
5.35k
            _ppdCacheDestroy(cache2);
318
5.35k
        }
319
6.26k
        if (attrs)
320
1
        {
321
1
            ippDelete(attrs);
322
1
        }
323
6.26k
    }
324
325
    // Test _cupsConvertOptions if we have enough segments
326
6.26k
    if (num_segments > 6 && seg_sizes[6] > 0)
327
603
    {
328
603
        ipp_t *request = ippNewRequest(IPP_OP_PRINT_JOB);
329
603
        if (request)
330
603
        {
331
603
            cups_option_t *options = NULL;
332
603
            int num_options = 0;
333
334
            // Parse options from segment data
335
603
            char options_str[1024];
336
603
            size_t copy_len = seg_sizes[6] < sizeof(options_str) - 1 ? seg_sizes[6] : sizeof(options_str) - 1;
337
603
            memcpy(options_str, segments[6], copy_len);
338
603
            options_str[copy_len] = '\0';
339
340
603
            num_options = cupsParseOptions(options_str, num_options, &options);
341
342
603
            if (num_options > 0)
343
590
            {
344
590
                _cupsConvertOptions(request, ppd, cache, NULL, NULL, NULL,
345
590
                                    "testuser", "application/pdf", 1, num_options, options);
346
590
                cupsFreeOptions(num_options, options);
347
590
            }
348
349
603
            ippDelete(request);
350
603
        }
351
603
    }
352
353
    // Cleanup resources
354
6.26k
    _ppdCacheDestroy(cache);
355
6.26k
    ppdClose(ppd);
356
6.26k
    cleanup_files();
357
358
6.26k
    return 0;
359
6.26k
}