Coverage Report

Created: 2025-11-11 07:03

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