/home/moyix/chaffctf/graphland/graphland.c
Line
Count
Source (jump to first uncovered line)
1
#include <stdio.h>
2
#include <stdint.h>
3
#include <stdlib.h>
4
#include <string.h>
5
#include <assert.h>
6
7
typedef struct _graph_node graph_node_t;
8
9
typedef struct _graph_node {
10
    int num_neighbors;
11
    int label;
12
    void *metadata;
13
    graph_node_t *hash_next;
14
    graph_node_t **neighbors;
15
} graph_node_t;
16
17
typedef enum _opcode {
18
    ADD_NODE,
19
    DEL_NODE,
20
    SET_METADATA,
21
    ADD_EDGE,
22
    DEL_EDGE,
23
    PRINT_GRAPH,
24
    PRINT_NODE,
25
    LAST_OPCODE
26
} opcode_t;
27
28
char *opcode_names[] = {
29
    "ADD_NODE",
30
    "DEL_NODE",
31
    "SET_METADATA",
32
    "ADD_EDGE",
33
    "DEL_EDGE",
34
    "PRINT_GRAPH",
35
    "PRINT_NODE"
36
};
37
38
// Handler prototypes
39
void add_node(uint8_t *, int);
40
void del_node(uint8_t *, int);
41
void set_metadata(uint8_t *, int);
42
void add_edge(uint8_t *, int);
43
void del_edge(uint8_t *, int);
44
void print_graph(uint8_t *, int);
45
void print_node(uint8_t *, int);
46
47
void (*opcode_handlers[])(uint8_t *, int) = {
48
    [ADD_NODE] = add_node,
49
    [DEL_NODE] = del_node,
50
    [SET_METADATA] = set_metadata,
51
    [ADD_EDGE] = add_edge,
52
    [DEL_EDGE] = del_edge,
53
    [PRINT_GRAPH] = print_graph,
54
    [PRINT_NODE] = print_node,
55
};
56
57
typedef struct _command {
58
    uint16_t opcode;
59
    uint16_t size;
60
    uint8_t *data;
61
} command_t;
62
63
353k
#define HASH_SIZE 128
64
struct graph_hashtable {
65
    graph_node_t *buckets[HASH_SIZE];
66
} graph = {};
67
68
126k
int hash_node(int label) {
69
126k
    if (label < 0) label = -label;
70
126k
    return label % HASH_SIZE;
71
126k
}
72
73
17.5k
void free_node(graph_node_t *node) {
74
17.5k
    free(node->metadata);
75
17.5k
    free(node->neighbors);
76
17.5k
    free(node);
77
17.5k
}
78
79
17.5k
void hash_insert(graph_node_t *node) {
80
17.5k
    if (node == NULL) return;
81
17.5k
    int nodehash = hash_node(node->label);
82
17.5k
    node->hash_next = NULL;
83
17.5k
    
84
17.5k
    graph_node_t *cursor = graph.buckets[nodehash];
85
17.5k
    if (!cursor) {
86
1.90k
        graph.buckets[nodehash] = node;
87
1.90k
        //printf("DEBUG: inserted label %d node %p\n", node->label, node);
88
1.90k
        return;
89
1.90k
    }
90
15.6k
    // Seek to the last element
91
15.6k
    graph_node_t *last;
92
401k
    while(cursor) {
93
391k
        // Don't allow duplicate labels
94
391k
        if (cursor->label == node->label) {
95
5.68k
            //printf("DEBUG: not adding duplicate node %d\n", node->label);
96
5.68k
            free_node(node);
97
5.68k
            return;
98
5.68k
        }
99
385k
        last = cursor;
100
385k
        cursor = cursor->hash_next;
101
385k
    }
102
15.6k
103
15.6k
    // Insert
104
15.6k
    last->hash_next = node;
105
9.94k
    //printf("DEBUG: inserted label %d node %p\n", node->label, node);
106
9.94k
    return;
107
15.6k
}
108
109
87.0k
graph_node_t * hash_lookup(int label) {
110
87.0k
    //printf("DEBUG: trying to look up label %d\n", label);
111
87.0k
    graph_node_t *cursor = graph.buckets[hash_node(label)];
112
2.78M
    while (cursor) {
113
2.78M
        if (cursor->label == label) break;
114
2.69M
        cursor = cursor->hash_next;
115
2.69M
    }
116
87.0k
    //printf("DEBUG: label %d => %p\n", label, cursor);
117
87.0k
    return cursor;
118
87.0k
}
119
120
// Remove node from the hash by label
121
// Does NOT free the node
122
11.2k
void hash_remove(int label) {
123
11.2k
    //printf("DEBUG: trying to remove label %d\n", label);
124
11.2k
    graph_node_t *cursor = graph.buckets[hash_node(label)];
125
11.2k
    // Didn't find it, bail
126
11.2k
    if (!cursor) return;
127
11.2k
    // Special case for first bucket entry
128
11.2k
    if (cursor->label == label) {
129
11.1k
        graph.buckets[hash_node(label)] = cursor->hash_next;
130
11.1k
        if (!cursor->hash_next) return;
131
9.91k
    }
132
395k
    while(cursor->hash_next) {
133
385k
       if (cursor->hash_next->label == label) {
134
45
           cursor->hash_next = cursor->hash_next->hash_next;
135
45
           return;
136
45
       }
137
385k
       cursor = cursor->hash_next;
138
385k
    }
139
9.91k
    return;
140
9.91k
}
141
142
643k
void add_node(uint8_t *data, int size) {
143
643k
    if (size != sizeof(int)) return;
144
17.5k
    int label = *(int *)data;
145
17.5k
    //printf("DEBUG: adding node with label %d\n", label);
146
17.5k
    graph_node_t *node = calloc(1, sizeof(graph_node_t));
147
17.5k
    if (!node) return;
148
17.5k
    node->label = label;
149
17.5k
    hash_insert(node);
150
17.5k
}
151
152
15.4k
void del_node(uint8_t *data, int size) {
153
15.4k
    if (size != sizeof(int)) return;
154
11.6k
    int label = *(int *)data;
155
11.6k
    //printf("DEBUG: deleting node with label %d\n", label);
156
11.6k
    graph_node_t *node = hash_lookup(label);
157
11.6k
    if (!node) return;
158
11.2k
    hash_remove(label);
159
11.2k
    free_node(node);
160
11.2k
}
161
162
1.93k
void set_metadata(uint8_t *data, int size) {
163
1.93k
    if (size < 2*sizeof(int)) return;
164
525
    int label = *(int *)data;
165
525
    int metadata_len = *(int *)(data + sizeof(int));
166
525
    //printf("DEBUG: setting metadata for node %d mdlen %d\n", label, metadata_len);
167
525
    if (size != 2*sizeof(int) + metadata_len) return;
168
357
    graph_node_t *node = hash_lookup(label);
169
357
    //printf("DEBUG: node lookup returned %p\n", node);
170
357
    if (!node) return;
171
269
    if (node->metadata) {
172
75
        //printf("DEBUG: node has pre-existing metadata, freeing\n");
173
75
        free(node->metadata);
174
75
        node->metadata = NULL;
175
75
    }
176
269
    uint8_t *metadata = malloc(metadata_len);
177
269
    if (!metadata) return;
178
269
    memcpy(metadata, data + 2*sizeof(int), metadata_len);
179
269
    node->metadata = metadata;
180
269
}
181
182
23.5k
void add_edge(uint8_t *data, int size) {
183
23.5k
    if (size != 2*sizeof(int)) return;
184
21.8k
    int src_label = *(int *)data;
185
21.8k
    int dst_label = *(int *)(data + sizeof(int));
186
21.8k
    graph_node_t *src_node = hash_lookup(src_label);
187
21.8k
    if (!src_node) return;
188
21.3k
    graph_node_t *dst_node = hash_lookup(dst_label);
189
21.3k
    if (!dst_node) return;
190
21.3k
191
21.3k
    src_node->num_neighbors++;
192
21.3k
    // Increase the neighbor array size
193
21.3k
    src_node->neighbors = realloc(src_node->neighbors,
194
21.3k
            src_node->num_neighbors * sizeof(graph_node_t *));
195
21.3k
    if (!src_node->neighbors) {
196
0
        src_node->num_neighbors--;
197
0
        return;
198
0
    }
199
21.3k
    src_node->neighbors[src_node->num_neighbors-1] = dst_node;
200
21.3k
}
201
202
19.6k
void del_edge(uint8_t *data, int size) {
203
19.6k
    if (size != 2*sizeof(int)) return;
204
10.6k
    int src_label = *(int *)data;
205
10.6k
    int dst_label = *(int *)(data + sizeof(int));
206
10.6k
    graph_node_t *src_node = hash_lookup(src_label);
207
10.6k
    if (!src_node) return;
208
10.5k
    graph_node_t *dst_node = hash_lookup(dst_label);
209
10.5k
    if (!dst_node) return;
210
10.4k
    int i;
211
20.4k
    for (i = 0; i < src_node->num_neighbors; i++) {
212
20.2k
        if (src_node->neighbors[i] == dst_node) break;
213
20.2k
    }
214
10.4k
    // If not found ignore
215
10.4k
    if (i == src_node->num_neighbors) return;
216
10.2k
    // Shift array down by one
217
10.2k
    memmove(src_node->neighbors+i,
218
10.2k
            src_node->neighbors+i+1,
219
10.2k
            src_node->num_neighbors-(i+1));
220
10.2k
    // Resize array
221
10.2k
    src_node->num_neighbors--;
222
10.2k
    graph_node_t **tmp = realloc(src_node->neighbors,
223
10.2k
            src_node->num_neighbors * sizeof(graph_node_t *));
224
10.2k
    // Need to distinguish between realloc failure and the case
225
10.2k
    // where we simply resized the array down to 0
226
10.2k
    if (!tmp && src_node->num_neighbors) {
227
0
        src_node->num_neighbors++;
228
0
        return;
229
0
    }
230
10.2k
    src_node->neighbors = tmp;
231
10.2k
}
232
233
4.72k
static void print_node_internal(graph_node_t *node) {
234
4.72k
#ifndef FUZZER
235
4.72k
    printf("[node @%p with label %d]\n", node, node->label);
236
4.72k
#endif
237
10.6k
    for (int i = 0; i < node->num_neighbors; i++) {
238
5.96k
#ifndef FUZZER
239
5.96k
        printf("[edge %d -> %d]\n", node->label, node->neighbors[i]->label);
240
5.96k
#endif
241
5.96k
    }
242
4.72k
}
243
244
1.43k
void print_graph(uint8_t *data, int size) {
245
1.43k
    graph_node_t *cursor;
246
185k
    for(int i = 0; i < HASH_SIZE; i++) {
247
183k
        cursor = graph.buckets[i];
248
188k
        while(cursor) {
249
4.24k
            print_node_internal(cursor);
250
4.24k
            cursor = cursor->hash_next;
251
4.24k
        }
252
183k
    }
253
1.43k
}
254
255
1.29k
void print_node(uint8_t *data, int size) {
256
1.29k
    if (size != sizeof(int)) return;
257
706
    int label = *(int *)data;
258
706
    graph_node_t *node = hash_lookup(label);
259
706
    if (!node) return;
260
480
    print_node_internal(node);
261
480
}
262
263
319
void free_graph() {
264
319
    graph_node_t *cursor;
265
41.1k
    for(int i = 0; i < HASH_SIZE; i++) {
266
40.8k
        cursor = graph.buckets[i];
267
41.4k
        while(cursor) {
268
655
            graph_node_t *tmp = cursor;
269
655
            cursor = cursor->hash_next;
270
655
            free_node(tmp);
271
655
        }
272
40.8k
        graph.buckets[i] = NULL;
273
40.8k
    }
274
319
}
275
276
1
void test_hashtable() {
277
10.0k
    for (int i = 0; i < 10000; i++) {
278
10.0k
        add_node((uint8_t *)&i, sizeof(i));
279
10.0k
    }
280
10.0k
    for (int i = 0; i < 10000-1; i++) {
281
9.99k
        int buf[2] = {i, i+1};
282
9.99k
        add_edge((uint8_t *)buf, sizeof(buf));
283
9.99k
        buf[0] = i+1;
284
9.99k
        buf[1] = i;
285
9.99k
        add_edge((uint8_t *)buf, sizeof(buf));
286
9.99k
    }
287
10.0k
    for (int i = 0; i < 10000-1; i++) {
288
9.99k
        int buf[2] = {i, i+1};
289
9.99k
        del_edge((uint8_t *)buf, sizeof(buf));
290
9.99k
    }
291
10.0k
    for (int i = 0; i < 10000; i++) {
292
10.0k
        assert(hash_lookup(i) != NULL);
293
10.0k
    }
294
10.0k
    for (int i = 0; i < 10000; i++) {
295
10.0k
        del_node((uint8_t *)&i, sizeof(i));
296
10.0k
    }
297
1
    free_graph();
298
1
}
299
300
#ifndef FUZZER
301
318
int main(int argc, char **argv) {
302
318
    int err = 0;
303
318
    if (argc < 2) {
304
1
        test_hashtable();
305
1
        fprintf(stderr, "usage: %s <file>\n", argv[0]);
306
1
        err = 1; goto cleanup;
307
1
    }
308
317
    FILE *f = fopen(argv[1], "rb");
309
317
    if (!f) {
310
1
        perror("fopen");
311
1
        err = 1; goto cleanup;
312
1
    }
313
822k
    while (!feof(f)) {
314
822k
        uint16_t opcode;
315
822k
        uint16_t size;
316
822k
        int n;
317
822k
        n = fread(&opcode, sizeof(uint16_t), 1, f);
318
822k
        if (!n) {
319
141
            fprintf(stderr, "failed to read from file\n");
320
141
            err = 1; goto cleanup;
321
141
        }
322
821k
        n = fread(&size, sizeof(uint16_t), 1, f);
323
821k
        if (!n) {
324
175
            fprintf(stderr, "failed to read from file\n");
325
175
            err = 1; goto cleanup;
326
175
        }
327
821k
        uint8_t *data = malloc(size);
328
821k
        if (!data) {
329
0
            perror("malloc");
330
0
            err = 1; goto cleanup;
331
0
        }
332
821k
        if (opcode >= LAST_OPCODE) {
333
164k
            fprintf(stderr, "note: invalid opcode, skipping\n");
334
164k
            free(data);
335
164k
            continue;
336
164k
        }
337
656k
        //printf("DEBUG: opcode %s size %d\n", opcode_names[opcode], size);
338
656k
        opcode_handlers[opcode](data, size);
339
656k
        free(data);
340
656k
    }
341
316
342
318
cleanup:
343
318
    free_graph();
344
318
345
318
    return err;
346
316
}
347
348
#else
349
350
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
351
    int i = 0;
352
    uint8_t *dp = (uint8_t *)Data;
353
    while (i < Size) {
354
        if (Size - i < sizeof(uint16_t)) goto cleanup;
355
        uint16_t opcode = *(uint16_t *)(dp+i); i += sizeof(uint16_t);
356
        if (Size - i < sizeof(uint16_t)) goto cleanup;
357
        uint16_t cmd_size = *(uint16_t *)(dp+i); i += sizeof(uint16_t);
358
        if (Size - i < cmd_size) goto cleanup;
359
        if (opcode >= LAST_OPCODE) {
360
            i += cmd_size;
361
            continue;
362
        }
363
        //printf("DEBUG: opcode %s size %d\n", opcode_names[opcode], cmd_size);
364
        opcode_handlers[opcode](dp+i, cmd_size);
365
        i += cmd_size;
366
    }
367
cleanup:
368
    free_graph();
369
    return 0;
370
}
371
372
#endif