/src/librabbitmq/fuzz/fuzz_codec.c
Line | Count | Source |
1 | | // Copyright 2007 - 2026, Arthur Chan and the rabbitmq-c contributors. |
2 | | // SPDX-License-Identifier: mit |
3 | | |
4 | | #include <stdint.h> |
5 | | #include <stdlib.h> |
6 | | #include <string.h> |
7 | | |
8 | | #include <rabbitmq-c/amqp.h> |
9 | | #include <rabbitmq-c/framing.h> |
10 | | |
11 | | // Round-trips the AMQP codecs. The existing fuzzers only decode; the generated |
12 | | // serializers (amqp_encode_method/properties/table) and amqp_table_clone are |
13 | | // unreachable from hand-written input. Each mode decodes attacker bytes into the |
14 | | // in-memory struct, then re-encodes/clones it -- the decoder supplies the |
15 | | // adversarial-but-valid structs the encoders would otherwise never see. |
16 | | |
17 | 7.28k | #define ENC_BUF_SIZE (1u << 20) |
18 | | |
19 | 4.33k | static void roundtrip_method(const uint8_t *p, size_t n) { |
20 | 4.33k | amqp_pool_t pool; |
21 | 4.33k | void *decoded = NULL; |
22 | 4.33k | amqp_method_number_t method_id; |
23 | 4.33k | amqp_bytes_t encoded; |
24 | | |
25 | 4.33k | if (n < 4) { |
26 | 4 | return; |
27 | 4 | } |
28 | 4.33k | method_id = ((amqp_method_number_t)p[0] << 24) | |
29 | 4.33k | ((amqp_method_number_t)p[1] << 16) | |
30 | 4.33k | ((amqp_method_number_t)p[2] << 8) | (amqp_method_number_t)p[3]; |
31 | 4.33k | encoded.len = n - 4; |
32 | 4.33k | encoded.bytes = (void *)(p + 4); |
33 | | |
34 | 4.33k | init_amqp_pool(&pool, 4096); |
35 | 4.33k | if (amqp_decode_method(method_id, &pool, encoded, &decoded) == |
36 | 4.33k | AMQP_STATUS_OK && |
37 | 2.32k | decoded != NULL) { |
38 | 2.32k | void *buf = malloc(ENC_BUF_SIZE); |
39 | 2.32k | if (buf != NULL) { |
40 | 2.32k | amqp_bytes_t out; |
41 | 2.32k | out.len = ENC_BUF_SIZE; |
42 | 2.32k | out.bytes = buf; |
43 | 2.32k | amqp_encode_method(method_id, decoded, out); |
44 | 2.32k | free(buf); |
45 | 2.32k | } |
46 | 2.32k | } |
47 | 4.33k | empty_amqp_pool(&pool); |
48 | 4.33k | } |
49 | | |
50 | 899 | static void roundtrip_properties(const uint8_t *p, size_t n) { |
51 | 899 | amqp_pool_t pool; |
52 | 899 | void *decoded = NULL; |
53 | 899 | uint16_t class_id; |
54 | 899 | amqp_bytes_t encoded; |
55 | | |
56 | 899 | if (n < 2) { |
57 | 2 | return; |
58 | 2 | } |
59 | 897 | class_id = ((uint16_t)p[0] << 8) | (uint16_t)p[1]; |
60 | 897 | encoded.len = n - 2; |
61 | 897 | encoded.bytes = (void *)(p + 2); |
62 | | |
63 | 897 | init_amqp_pool(&pool, 4096); |
64 | 897 | if (amqp_decode_properties(class_id, &pool, encoded, &decoded) == |
65 | 897 | AMQP_STATUS_OK && |
66 | 582 | decoded != NULL) { |
67 | 582 | void *buf = malloc(ENC_BUF_SIZE); |
68 | 582 | if (buf != NULL) { |
69 | 582 | amqp_bytes_t out; |
70 | 582 | out.len = ENC_BUF_SIZE; |
71 | 582 | out.bytes = buf; |
72 | 582 | amqp_encode_properties(class_id, decoded, out); |
73 | 582 | free(buf); |
74 | 582 | } |
75 | 582 | } |
76 | 897 | empty_amqp_pool(&pool); |
77 | 897 | } |
78 | | |
79 | 1.08k | static void roundtrip_table(const uint8_t *p, size_t n) { |
80 | 1.08k | amqp_pool_t pool; |
81 | 1.08k | amqp_table_t decoded; |
82 | 1.08k | amqp_bytes_t encoded; |
83 | 1.08k | size_t offset = 0; |
84 | | |
85 | 1.08k | encoded.len = n; |
86 | 1.08k | encoded.bytes = (void *)p; |
87 | | |
88 | 1.08k | init_amqp_pool(&pool, 4096); |
89 | 1.08k | memset(&decoded, 0, sizeof(decoded)); |
90 | 1.08k | if (amqp_decode_table(encoded, &pool, &decoded, &offset) == AMQP_STATUS_OK) { |
91 | 741 | void *buf = malloc(ENC_BUF_SIZE); |
92 | 741 | if (buf != NULL) { |
93 | 741 | amqp_bytes_t out; |
94 | 741 | size_t out_off = 0; |
95 | 741 | out.len = ENC_BUF_SIZE; |
96 | 741 | out.bytes = buf; |
97 | 741 | amqp_encode_table(out, &decoded, &out_off); |
98 | 741 | free(buf); |
99 | 741 | } |
100 | 741 | { |
101 | 741 | amqp_pool_t clone_pool; |
102 | 741 | amqp_table_t clone; |
103 | 741 | init_amqp_pool(&clone_pool, 4096); |
104 | 741 | memset(&clone, 0, sizeof(clone)); |
105 | 741 | amqp_table_clone(&decoded, &clone, &clone_pool); |
106 | 741 | empty_amqp_pool(&clone_pool); |
107 | 741 | } |
108 | 741 | } |
109 | 1.08k | empty_amqp_pool(&pool); |
110 | 1.08k | } |
111 | | |
112 | | // First byte selects the codec; the remainder is the payload, framed like the |
113 | | // matching decode fuzzer (method-id / class-id prefix where applicable). |
114 | 6.32k | extern int LLVMFuzzerTestOneInput(const char *data, size_t size) { |
115 | 6.32k | const uint8_t *bytes = (const uint8_t *)data; |
116 | | |
117 | 6.32k | if (size < 1) { |
118 | 0 | return 0; |
119 | 0 | } |
120 | 6.32k | switch (bytes[0] % 3) { |
121 | 4.33k | case 0: |
122 | 4.33k | roundtrip_method(bytes + 1, size - 1); |
123 | 4.33k | break; |
124 | 899 | case 1: |
125 | 899 | roundtrip_properties(bytes + 1, size - 1); |
126 | 899 | break; |
127 | 1.08k | default: |
128 | 1.08k | roundtrip_table(bytes + 1, size - 1); |
129 | 1.08k | break; |
130 | 6.32k | } |
131 | 6.32k | return 0; |
132 | 6.32k | } |