/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 |