| /* |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| */ |
| |
| #define _POSIX_C_SOURCE 200809L |
| |
| #include <proton/connection.h> |
| #include <proton/connection_driver.h> |
| #include <proton/delivery.h> |
| #include <proton/link.h> |
| #include <proton/message.h> |
| #include <proton/proactor.h> |
| #include <proton/session.h> |
| #include <proton/transport.h> |
| #include <proton/url.h> |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <time.h> |
| #include <unistd.h> |
| |
| #include <arpa/inet.h> |
| #include <errno.h> |
| #include <errno.h> |
| #include <strings.h> |
| #include <sys/socket.h> |
| #include <sys/wait.h> |
| |
| #include "libFuzzingEngine.h" |
| |
| // bool VERBOSE = true; |
| bool VERBOSE = false; |
| // bool ERRORS = true; |
| bool ERRORS = false; |
| |
| #define MAX_SIZE 1024 |
| |
| typedef char str[MAX_SIZE]; |
| |
| typedef struct app_data_t { |
| str address; |
| str container_id; |
| pn_rwbytes_t message_buffer; |
| int message_count; |
| int received; |
| pn_proactor_t *proactor; |
| bool finished; |
| } app_data_t; |
| |
| static const int BATCH = 1000; /* Batch size for unlimited receive */ |
| |
| static int exit_code = 0; |
| |
| static void check_condition(pn_event_t *e, pn_condition_t *cond) { |
| if (VERBOSE) |
| printf("beginning check_condition\n"); |
| if (pn_condition_is_set(cond)) { |
| exit_code = 1; |
| if (VERBOSE || ERRORS) |
| fprintf(stderr, "%s: %s: %s\n", pn_event_type_name(pn_event_type(e)), |
| pn_condition_get_name(cond), pn_condition_get_description(cond)); |
| } |
| } |
| |
| static void decode_message(pn_delivery_t *dlv) { |
| static char buffer[MAX_SIZE]; |
| ssize_t len; |
| // try to decode the message body |
| if (pn_delivery_pending(dlv) < MAX_SIZE) { |
| // read in the raw data |
| len = pn_link_recv(pn_delivery_link(dlv), buffer, MAX_SIZE); |
| if (len > 0) { |
| // decode it into a proton message |
| pn_message_t *m = pn_message(); |
| if (PN_OK == pn_message_decode(m, buffer, len)) { |
| pn_string_t *s = pn_string(NULL); |
| pn_inspect(pn_message_body(m), s); |
| printf("%s\n", pn_string_get(s)); |
| pn_free(s); |
| } |
| pn_message_free(m); |
| } |
| } |
| } |
| |
| static void handle(app_data_t *app, pn_event_t *event) { |
| switch (pn_event_type(event)) { |
| |
| case PN_CONNECTION_INIT: { |
| pn_connection_t *c = pn_event_connection(event); |
| pn_connection_set_container(c, app->container_id); |
| pn_connection_open(c); |
| pn_session_t *s = pn_session(c); |
| pn_session_open(s); |
| pn_link_t *l = pn_receiver(s, "my_receiver"); |
| pn_terminus_set_address(pn_link_source(l), app->address); |
| pn_link_open(l); |
| /* cannot receive without granting credit: */ |
| pn_link_flow(l, app->message_count ? app->message_count : BATCH); |
| } break; |
| |
| case PN_DELIVERY: { |
| /* A message has been received */ |
| pn_link_t *link = NULL; |
| pn_delivery_t *dlv = pn_event_delivery(event); |
| if (pn_delivery_readable(dlv) && !pn_delivery_partial(dlv)) { |
| link = pn_delivery_link(dlv); |
| decode_message(dlv); |
| /* Accept the delivery */ |
| pn_delivery_update(dlv, PN_ACCEPTED); |
| /* done with the delivery, move to the next and free it */ |
| pn_link_advance(link); |
| pn_delivery_settle(dlv); /* dlv is now freed */ |
| |
| if (app->message_count == 0) { |
| /* receive forever - see if more credit is needed */ |
| if (pn_link_credit(link) < BATCH / 2) { |
| /* Grant enough credit to bring it up to BATCH: */ |
| pn_link_flow(link, BATCH - pn_link_credit(link)); |
| } |
| } else if (++app->received >= app->message_count) { |
| /* done receiving, close the endpoints */ |
| printf("%d messages received\n", app->received); |
| pn_session_t *ssn = pn_link_session(link); |
| pn_link_close(link); |
| pn_session_close(ssn); |
| pn_connection_close(pn_session_connection(ssn)); |
| } |
| } |
| } break; |
| |
| case PN_TRANSPORT_ERROR: |
| check_condition(event, pn_transport_condition(pn_event_transport(event))); |
| pn_connection_close(pn_event_connection(event)); |
| break; |
| |
| case PN_CONNECTION_REMOTE_CLOSE: |
| check_condition(event, |
| pn_connection_remote_condition(pn_event_connection(event))); |
| pn_connection_close(pn_event_connection(event)); |
| break; |
| |
| case PN_SESSION_REMOTE_CLOSE: |
| check_condition(event, |
| pn_session_remote_condition(pn_event_session(event))); |
| pn_connection_close(pn_event_connection(event)); |
| break; |
| |
| case PN_LINK_REMOTE_CLOSE: |
| case PN_LINK_REMOTE_DETACH: |
| check_condition(event, pn_link_remote_condition(pn_event_link(event))); |
| pn_connection_close(pn_event_connection(event)); |
| break; |
| |
| case PN_PROACTOR_INACTIVE: |
| app->finished = true; |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| double now(void) { |
| struct timespec spec; |
| if (clock_gettime(CLOCK_MONOTONIC, &spec) != 0) { |
| perror("clock_gettime"); |
| exit(errno); |
| } |
| return (double)spec.tv_sec + (double)spec.tv_nsec / 1000000.0; |
| } |
| |
| int sut(void) { |
| /* Default values for application and connection. */ |
| app_data_t app = {{0}}; |
| app.message_count = 2; |
| |
| snprintf(app.container_id, sizeof(app.container_id), "%s:%d", |
| "fuzz_proactor_recv", getpid()); |
| |
| const char *address = "127.0.0.1:amqp"; |
| strncpy(app.address, "jms.queue.example", sizeof(app.address)); |
| |
| if (VERBOSE) |
| printf("before proactor\n"); |
| /* Create the proactor and connect */ |
| app.proactor = pn_proactor(); |
| pn_proactor_connect(app.proactor, pn_connection(), address); |
| |
| if (VERBOSE) |
| printf("before loop\n"); |
| double thence = now(); |
| do { |
| if (VERBOSE) |
| printf("before set proactor timeout\n"); |
| pn_proactor_set_timeout(app.proactor, 100); |
| if (VERBOSE) |
| printf("before proactor wait\n"); |
| pn_event_batch_t *events = pn_proactor_wait(app.proactor); |
| pn_event_t *e; |
| if (VERBOSE) |
| printf("before proactor next batch\n"); |
| while ((e = pn_event_batch_next(events))) { |
| handle(&app, e); |
| } |
| pn_proactor_done(app.proactor, events); |
| |
| if (VERBOSE) |
| printf("before reloop\n"); |
| double deltat = now() - thence; |
| if (VERBOSE) |
| printf("deltat %f", deltat); |
| if (deltat > 1) { |
| app.finished = true; |
| } |
| } while (!app.finished); |
| |
| if (VERBOSE) |
| printf("after loop\n"); |
| pn_proactor_free(app.proactor); |
| free(app.message_buffer.start); |
| return exit_code; |
| } |
| |
| void serve_data(const uint8_t *Data, size_t Size) { |
| int sockfd; |
| if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { |
| perror("socket"); |
| _Exit(errno); |
| } |
| int reuseaddr = 1; |
| if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, |
| sizeof(reuseaddr)) == -1) { |
| perror("setsockopt"); |
| _Exit(errno); |
| } |
| struct sockaddr_in self; |
| memset(&self, 0, sizeof(self)); |
| self.sin_family = AF_INET; |
| self.sin_port = htons(5672); |
| self.sin_addr.s_addr = INADDR_ANY; |
| if (bind(sockfd, (struct sockaddr *)&self, sizeof(self)) != 0) { |
| perror("bind"); |
| |
| // Lets unblock the old child that listens by starting new client to read |
| // from it. It breaks the fuzzing somewhat, but it is better to mess up one |
| // than many inputs. |
| if (VERBOSE) |
| printf("unblocking old bound child\n"); |
| kill(getppid(), SIGUSR1); |
| |
| _Exit(errno); |
| } |
| if (VERBOSE) |
| printf("bound\n"); |
| if (listen(sockfd, 1) != 0) { |
| perror("listen"); |
| _Exit(errno); |
| } |
| |
| if (VERBOSE) |
| printf("listened, lets run sut\n"); |
| kill(getppid(), SIGUSR1); |
| |
| struct sockaddr_in client_addr; |
| socklen_t addrlen = sizeof(client_addr); |
| int clientfd = accept(sockfd, (struct sockaddr *)&client_addr, &addrlen); |
| if (VERBOSE) |
| printf("%s:%d connected\n", inet_ntoa(client_addr.sin_addr), |
| ntohs(client_addr.sin_port)); |
| if (VERBOSE) |
| printf("will send\n"); |
| send(clientfd, Data, Size, 0); |
| // sleep(1); |
| close(clientfd); |
| close(sockfd); |
| if (VERBOSE) |
| printf("done serving\n"); |
| } |
| |
| void run_sut(int s) { |
| if (VERBOSE) |
| printf("running sut\n"); |
| sut(); |
| if (VERBOSE) |
| printf("finished running sut\n"); |
| } |
| |
| void signal_callback_handler(int signum) { |
| if (VERBOSE) |
| printf("Caught signal SIGPIPE %d\n", signum); |
| } |
| |
| bool DoInitialization(void) { |
| struct sigaction sa; |
| sa.sa_handler = run_sut; |
| sigemptyset(&sa.sa_mask); |
| sa.sa_flags = SA_RESTART; /* Restart functions if interrupted by handler */ |
| if (sigaction(SIGUSR1, &sa, NULL) == -1) { |
| perror("sigaction"); |
| exit(2); |
| } |
| sa.sa_handler = signal_callback_handler; |
| if (sigaction(SIGPIPE, &sa, NULL) == -1) { |
| perror("sigaction"); |
| exit(2); |
| } |
| return true; |
| } |
| |
| int LLVMFuzzerInitialize(int *argc, char ***argv) { |
| DoInitialization(); |
| return 0; |
| } |
| |
| int prev_pid = 0; |
| |
| int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { |
| // sometimes, esp. with AFL, but libFuzz too, |
| // the old socket is still bound for new run and |
| // it skips all new runs... |
| if (prev_pid != 0) { |
| kill(SIGKILL, prev_pid); |
| } |
| |
| pid_t pid = fork(); |
| if (pid < 0) { |
| perror("fork"); |
| exit(errno); |
| } |
| if (pid == 0) { // child |
| serve_data(Data, Size); |
| _Exit(0); |
| } else { // parent |
| prev_pid = pid; |
| if (VERBOSE) |
| printf("waiting for child\n"); |
| siginfo_t status; |
| waitid(P_PID, pid, &status, WEXITED); |
| if (VERBOSE) |
| printf("finished waiting for child\n"); |
| } |
| return 0; |
| } |