/src/librabbitmq/fuzz/fuzz_read_message.c
Line | Count | Source |
1 | | // Copyright 2007 - 2026, Alan Antonuk 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 | | #include "amqp_socket.h" |
12 | | |
13 | 11.3k | #define MAX_INPUT_SIZE (1U << 20) |
14 | 239k | #define MAX_BODY_SIZE (1U << 20) |
15 | | |
16 | | struct fuzz_socket_t { |
17 | | const struct amqp_socket_class_t *klass; |
18 | | const uint8_t *data; |
19 | | size_t size; |
20 | | size_t offset; |
21 | | }; |
22 | | |
23 | | static ssize_t fuzz_socket_send(AMQP_UNUSED void *base, |
24 | | AMQP_UNUSED const void *buf, size_t len, |
25 | 0 | AMQP_UNUSED int flags) { |
26 | 0 | return (ssize_t)len; |
27 | 0 | } |
28 | | |
29 | | static ssize_t fuzz_socket_recv(void *base, void *buf, size_t len, |
30 | 7.74k | AMQP_UNUSED int flags) { |
31 | 7.74k | struct fuzz_socket_t *socket = (struct fuzz_socket_t *)base; |
32 | 7.74k | size_t remaining = socket->size - socket->offset; |
33 | 7.74k | size_t count; |
34 | | |
35 | 7.74k | if (remaining == 0) { |
36 | 2.07k | return AMQP_STATUS_CONNECTION_CLOSED; |
37 | 2.07k | } |
38 | | |
39 | 5.66k | count = remaining < len ? remaining : len; |
40 | 5.66k | memcpy(buf, socket->data + socket->offset, count); |
41 | 5.66k | socket->offset += count; |
42 | 5.66k | return (ssize_t)count; |
43 | 7.74k | } |
44 | | |
45 | | static int fuzz_socket_open(AMQP_UNUSED void *base, |
46 | | AMQP_UNUSED const char *host, |
47 | | AMQP_UNUSED int port, |
48 | 0 | AMQP_UNUSED const struct timeval *timeout) { |
49 | 0 | return AMQP_STATUS_OK; |
50 | 0 | } |
51 | | |
52 | | static int fuzz_socket_close(AMQP_UNUSED void *base, |
53 | 0 | AMQP_UNUSED amqp_socket_close_enum force) { |
54 | 0 | return AMQP_STATUS_OK; |
55 | 0 | } |
56 | | |
57 | 2.07k | static int fuzz_socket_get_sockfd(AMQP_UNUSED void *base) { return 0; } |
58 | | |
59 | 5.57k | static void fuzz_socket_delete(void *base) { free(base); } |
60 | | |
61 | | static const struct amqp_socket_class_t fuzz_socket_class = { |
62 | | fuzz_socket_send, fuzz_socket_recv, fuzz_socket_open, |
63 | | fuzz_socket_close, fuzz_socket_get_sockfd, fuzz_socket_delete}; |
64 | | |
65 | 239k | static uint64_t read_be64(const uint8_t *data) { |
66 | 239k | uint64_t value = 0; |
67 | 239k | size_t i; |
68 | | |
69 | 2.15M | for (i = 0; i < 8; i++) { |
70 | 1.91M | value = (value << 8) | data[i]; |
71 | 1.91M | } |
72 | 239k | return value; |
73 | 239k | } |
74 | | |
75 | 5.65k | static int body_sizes_are_bounded(const uint8_t *data, size_t size) { |
76 | 5.65k | size_t offset = 0; |
77 | | |
78 | 5.65k | if (size >= 8 && memcmp(data, "AMQP", 4) == 0) { |
79 | 102 | offset = 8; |
80 | 102 | } |
81 | | |
82 | 390k | while (size - offset >= 8) { |
83 | 385k | uint32_t payload_size = ((uint32_t)data[offset + 3] << 24) | |
84 | 385k | ((uint32_t)data[offset + 4] << 16) | |
85 | 385k | ((uint32_t)data[offset + 5] << 8) | |
86 | 385k | (uint32_t)data[offset + 6]; |
87 | 385k | size_t frame_size = (size_t)payload_size + 8; |
88 | | |
89 | 385k | if (frame_size > size - offset) { |
90 | 439 | break; |
91 | 439 | } |
92 | 385k | if (data[offset] == AMQP_FRAME_HEADER && payload_size >= 12 && |
93 | 239k | read_be64(data + offset + 11) > MAX_BODY_SIZE) { |
94 | 78 | return 0; |
95 | 78 | } |
96 | 385k | offset += frame_size; |
97 | 385k | } |
98 | | |
99 | 5.57k | return 1; |
100 | 5.65k | } |
101 | | |
102 | 5.65k | extern int LLVMFuzzerTestOneInput(const char *data, size_t size) { |
103 | 5.65k | amqp_connection_state_t connection; |
104 | 5.65k | amqp_message_t message; |
105 | 5.65k | struct fuzz_socket_t *socket; |
106 | 5.65k | amqp_rpc_reply_t reply; |
107 | | |
108 | 5.65k | if (size == 0 || size > MAX_INPUT_SIZE || |
109 | 5.65k | !body_sizes_are_bounded((const uint8_t *)data, size)) { |
110 | 78 | return 0; |
111 | 78 | } |
112 | | |
113 | 5.57k | connection = amqp_new_connection(); |
114 | 5.57k | if (connection == NULL) { |
115 | 0 | return 0; |
116 | 0 | } |
117 | | |
118 | 5.57k | socket = (struct fuzz_socket_t *)calloc(1, sizeof(*socket)); |
119 | 5.57k | if (socket == NULL) { |
120 | 0 | amqp_destroy_connection(connection); |
121 | 0 | return 0; |
122 | 0 | } |
123 | | |
124 | 5.57k | socket->klass = &fuzz_socket_class; |
125 | 5.57k | socket->data = (const uint8_t *)data; |
126 | 5.57k | socket->size = size; |
127 | 5.57k | amqp_set_socket(connection, (amqp_socket_t *)socket); |
128 | | |
129 | 5.57k | reply = amqp_read_message(connection, 1, &message, 0); |
130 | 5.57k | if (reply.reply_type == AMQP_RESPONSE_NORMAL) { |
131 | 22 | amqp_destroy_message(&message); |
132 | 22 | } |
133 | | |
134 | 5.57k | amqp_destroy_connection(connection); |
135 | 5.57k | return 0; |
136 | 5.57k | } |