Coverage Report

Created: 2026-02-26 06:24

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
15.5k
{
19
15.5k
    if (g_ppd_file)
20
7.79k
    {
21
7.79k
        unlink(g_ppd_file);
22
7.79k
        free(g_ppd_file);
23
7.79k
        g_ppd_file = NULL;
24
7.79k
    }
25
15.5k
    if (g_cache_file)
26
7.79k
    {
27
7.79k
        unlink(g_cache_file);
28
7.79k
        free(g_cache_file);
29
7.79k
        g_cache_file = NULL;
30
7.79k
    }
31
15.5k
}
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
7.79k
{
37
7.79k
    if (size < 4)
38
0
        return 0;
39
40
7.79k
    uint32_t num_segments = *(uint32_t *)data;
41
7.79k
    if (num_segments == 0 || num_segments > max_segments)
42
47
        return 0;
43
44
7.74k
    const uint8_t *ptr = data + 4;
45
7.74k
    size_t remaining = size - 4;
46
47
20.1k
    for (uint32_t i = 0; i < num_segments && i < max_segments; i++)
48
17.8k
    {
49
17.8k
        if (remaining < 4)
50
3.03k
            return i;
51
52
14.8k
        uint32_t seg_len = *(uint32_t *)ptr;
53
14.8k
        ptr += 4;
54
14.8k
        remaining -= 4;
55
56
14.8k
        if (seg_len > remaining)
57
2.38k
            return i;
58
59
12.4k
        segments[i] = ptr;
60
12.4k
        seg_sizes[i] = seg_len;
61
12.4k
        ptr += seg_len;
62
12.4k
        remaining -= seg_len;
63
12.4k
    }
64
65
2.33k
    return num_segments;
66
7.74k
}
67
68
// Create a simple IPP job for testing
69
static ipp_t *create_test_job(const uint8_t *data, size_t size)
70
732
{
71
732
    ipp_t *job = ippNew();
72
732
    if (!job)
73
0
        return NULL;
74
75
    // Add some basic attributes using fuzz data
76
732
    if (size > 10)
77
464
    {
78
464
        char media_name[64];
79
464
        snprintf(media_name, sizeof(media_name), "media-%02x%02x", data[0], data[1]);
80
464
        ippAddString(job, IPP_TAG_JOB, IPP_TAG_KEYWORD, "media", NULL, media_name);
81
464
    }
82
83
732
    if (size > 20)
84
416
    {
85
416
        char source_name[64];
86
416
        snprintf(source_name, sizeof(source_name), "source-%02x%02x", data[10], data[11]);
87
416
        ippAddString(job, IPP_TAG_JOB, IPP_TAG_KEYWORD, "media-source", NULL, source_name);
88
416
    }
89
90
732
    if (size > 30)
91
236
    {
92
236
        char type_name[64];
93
236
        snprintf(type_name, sizeof(type_name), "type-%02x%02x", data[20], data[21]);
94
236
        ippAddString(job, IPP_TAG_JOB, IPP_TAG_KEYWORD, "media-type", NULL, type_name);
95
236
    }
96
97
732
    return job;
98
732
}
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
10.6k
{
103
10.6k
    if (!pc)
104
0
        return;
105
106
    // Test _ppdCacheGetBin
107
10.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
10.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
10.6k
    if (num_segments > 3 && seg_sizes[3] > 0)
132
65
    {
133
65
        char input_slot[256];
134
65
        size_t copy_len = seg_sizes[3] < sizeof(input_slot) - 1 ? seg_sizes[3] : sizeof(input_slot) - 1;
135
65
        memcpy(input_slot, segments[3], copy_len);
136
65
        input_slot[copy_len] = '\0';
137
138
65
        const char *result = _ppdCacheGetSource(pc, input_slot);
139
65
        (void)result;
140
65
    }
141
142
    // Test _ppdCacheGetType
143
10.6k
    if (num_segments > 2 && seg_sizes[2] > 0)
144
62
    {
145
62
        char media_type[256];
146
62
        size_t copy_len = seg_sizes[2] < sizeof(media_type) - 1 ? seg_sizes[2] : sizeof(media_type) - 1;
147
62
        memcpy(media_type, segments[2], copy_len);
148
62
        media_type[copy_len] = '\0';
149
150
62
        const char *result = _ppdCacheGetType(pc, media_type);
151
62
        (void)result;
152
62
    }
153
154
    // Test _ppdCacheGetPageSize with keyword
155
10.6k
    if (num_segments > 1 && seg_sizes[1] > 0)
156
1.08k
    {
157
1.08k
        char page_size[256];
158
1.08k
        size_t copy_len = seg_sizes[1] < sizeof(page_size) - 1 ? seg_sizes[1] : sizeof(page_size) - 1;
159
1.08k
        memcpy(page_size, segments[1], copy_len);
160
1.08k
        page_size[copy_len] = '\0';
161
162
1.08k
        int exact = 0;
163
1.08k
        const char *result = _ppdCacheGetPageSize(pc, NULL, page_size, &exact);
164
1.08k
        (void)result;
165
1.08k
    }
166
167
    // Test _ppdCacheGetPageSize with IPP job
168
10.6k
    if (num_segments > 5 && seg_sizes[5] > 0)
169
183
    {
170
183
        ipp_t *job = create_test_job(segments[5], seg_sizes[5]);
171
183
        if (job)
172
183
        {
173
183
            int exact = 0;
174
183
            const char *result = _ppdCacheGetPageSize(pc, job, NULL, &exact);
175
183
            (void)result;
176
183
            ippDelete(job);
177
183
        }
178
183
    }
179
180
    // Test _ppdCacheGetInputSlot
181
10.6k
    if (num_segments > 5 && seg_sizes[5] > 0)
182
183
    {
183
183
        ipp_t *job = create_test_job(segments[5], seg_sizes[5]);
184
183
        if (job)
185
183
        {
186
183
            const char *result = _ppdCacheGetInputSlot(pc, job, NULL);
187
183
            (void)result;
188
183
            ippDelete(job);
189
183
        }
190
183
    }
191
192
    // Test _ppdCacheGetMediaType
193
10.6k
    if (num_segments > 5 && seg_sizes[5] > 0)
194
183
    {
195
183
        ipp_t *job = create_test_job(segments[5], seg_sizes[5]);
196
183
        if (job)
197
183
        {
198
183
            const char *result = _ppdCacheGetMediaType(pc, job, NULL);
199
183
            (void)result;
200
183
            ippDelete(job);
201
183
        }
202
183
    }
203
204
    // Test _ppdCacheGetSize
205
10.6k
    if (num_segments > 1 && seg_sizes[1] > 0)
206
1.08k
    {
207
1.08k
        char page_size[256];
208
1.08k
        size_t copy_len = seg_sizes[1] < sizeof(page_size) - 1 ? seg_sizes[1] : sizeof(page_size) - 1;
209
1.08k
        memcpy(page_size, segments[1], copy_len);
210
1.08k
        page_size[copy_len] = '\0';
211
212
1.08k
        pwg_size_t *result = _ppdCacheGetSize(pc, page_size, NULL);
213
1.08k
        (void)result;
214
1.08k
    }
215
216
    // Test _ppdCacheGetFinishingValues
217
10.6k
    if (ppd)
218
10.6k
    {
219
10.6k
        int finishings[20];
220
10.6k
        int num_finishings = _ppdCacheGetFinishingValues(ppd, pc, 20, finishings);
221
10.6k
        (void)num_finishings;
222
10.6k
    }
223
224
    // Test _ppdCacheGetFinishingOptions
225
10.6k
    if (num_segments > 5 && seg_sizes[5] > 0)
226
183
    {
227
183
        ipp_t *job = create_test_job(segments[5], seg_sizes[5]);
228
183
        if (job)
229
183
        {
230
183
            cups_option_t *options = NULL;
231
183
            int num_options = 0;
232
183
            num_options = _ppdCacheGetFinishingOptions(pc, job, IPP_FINISHINGS_NONE, num_options, &options);
233
234
183
            if (options)
235
0
            {
236
0
                cupsFreeOptions(num_options, options);
237
0
            }
238
183
            ippDelete(job);
239
183
        }
240
183
    }
241
10.6k
}
242
243
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
244
7.80k
{
245
    // Minimum size check
246
7.80k
    if (size < 100)
247
13
    {
248
13
        return 0;
249
13
    }
250
251
    // Setup cleanup
252
7.79k
    atexit(cleanup_files);
253
254
    // Create filenames with cursor_test_ prefix
255
7.79k
    g_ppd_file = malloc(256);
256
7.79k
    g_cache_file = malloc(256);
257
7.79k
    if (!g_ppd_file || !g_cache_file)
258
0
    {
259
0
        cleanup_files();
260
0
        return 0;
261
0
    }
262
263
7.79k
    snprintf(g_ppd_file, 256, "/tmp/cursor_test_ppd_%d.ppd", getpid());
264
7.79k
    snprintf(g_cache_file, 256, "/tmp/cursor_test_cache_%d.cache", getpid());
265
266
    // Parse input segments
267
7.79k
    const uint8_t *segments[10];
268
7.79k
    size_t seg_sizes[10];
269
7.79k
    int num_segments = parse_input_segments(data, size, segments, seg_sizes, 10);
270
271
7.79k
    if (num_segments < 1)
272
47
    {
273
47
        cleanup_files();
274
47
        return 0;
275
47
    }
276
277
    // Write PPD file data
278
7.74k
    FILE *file = fopen(g_ppd_file, "wb");
279
7.74k
    if (!file)
280
0
    {
281
0
        cleanup_files();
282
0
        return 0;
283
0
    }
284
285
7.74k
    fwrite(segments[0], 1, seg_sizes[0], file);
286
7.74k
    fclose(file);
287
288
    // Open PPD file
289
7.74k
    ppd_file_t *ppd = ppdOpenFile(g_ppd_file);
290
7.74k
    if (!ppd)
291
1.98k
    {
292
1.98k
        cleanup_files();
293
1.98k
        return 0;
294
1.98k
    }
295
296
    // Create cache from PPD
297
5.75k
    _ppd_cache_t *cache = _ppdCacheCreateWithPPD(NULL, ppd);
298
5.75k
    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
5.75k
    test_cache_functions(cache, ppd, segments, seg_sizes, num_segments);
307
308
    // Test cache file write/read cycle
309
5.75k
    if (_ppdCacheWriteFile(cache, g_cache_file, NULL))
310
5.75k
    {
311
5.75k
        ipp_t *attrs = NULL;
312
5.75k
        _ppd_cache_t *cache2 = _ppdCacheCreateWithFile(g_cache_file, &attrs);
313
5.75k
        if (cache2)
314
4.90k
        {
315
            // Test functions on loaded cache
316
4.90k
            test_cache_functions(cache2, ppd, segments, seg_sizes, num_segments);
317
4.90k
            _ppdCacheDestroy(cache2);
318
4.90k
        }
319
5.75k
        if (attrs)
320
1
        {
321
1
            ippDelete(attrs);
322
1
        }
323
5.75k
    }
324
325
    // Test _cupsConvertOptions if we have enough segments
326
5.75k
    if (num_segments > 6 && seg_sizes[6] > 0)
327
572
    {
328
572
        ipp_t *request = ippNewRequest(IPP_OP_PRINT_JOB);
329
572
        if (request)
330
572
        {
331
572
            cups_option_t *options = NULL;
332
572
            int num_options = 0;
333
334
            // Parse options from segment data
335
572
            char options_str[1024];
336
572
            size_t copy_len = seg_sizes[6] < sizeof(options_str) - 1 ? seg_sizes[6] : sizeof(options_str) - 1;
337
572
            memcpy(options_str, segments[6], copy_len);
338
572
            options_str[copy_len] = '\0';
339
340
572
            num_options = cupsParseOptions(options_str, num_options, &options);
341
342
572
            if (num_options > 0)
343
547
            {
344
547
                _cupsConvertOptions(request, ppd, cache, NULL, NULL, NULL,
345
547
                                    "testuser", "application/pdf", 1, num_options, options);
346
547
                cupsFreeOptions(num_options, options);
347
547
            }
348
349
572
            ippDelete(request);
350
572
        }
351
572
    }
352
353
    // Cleanup resources
354
5.75k
    _ppdCacheDestroy(cache);
355
5.75k
    ppdClose(ppd);
356
5.75k
    cleanup_files();
357
358
5.75k
    return 0;
359
5.75k
}