Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Add fuzzing tests #402

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 15 additions & 14 deletions src/modbus.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ int modbus_flush(modbus_t *ctx)
static unsigned int compute_response_length_from_request(modbus_t *ctx, uint8_t *req)
{
int length;
const int offset = ctx->backend->header_length;
const unsigned int offset = ctx->backend->header_length;

switch (req[offset]) {
case MODBUS_FC_READ_COILS:
Expand Down Expand Up @@ -343,7 +343,7 @@ int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type)
fd_set rset;
struct timeval tv;
struct timeval *p_tv;
int length_to_read;
unsigned int length_to_read;
int msg_length = 0;
_step_t step;

Expand Down Expand Up @@ -448,7 +448,7 @@ int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type)
case _STEP_META:
length_to_read = compute_data_length_after_meta(
ctx, msg, msg_type);
if ((msg_length + length_to_read) > (int)ctx->backend->max_adu_length) {
if ((msg_length + length_to_read) > ctx->backend->max_adu_length) {
errno = EMBBADDATA;
_error_print(ctx, "too many data");
return -1;
Expand Down Expand Up @@ -513,7 +513,7 @@ static int check_confirmation(modbus_t *ctx, uint8_t *req,
{
int rc;
int rsp_length_computed;
const int offset = ctx->backend->header_length;
const unsigned int offset = ctx->backend->header_length;
const int function = rsp[offset];

if (ctx->backend->pre_check_confirmation) {
Expand All @@ -531,7 +531,7 @@ static int check_confirmation(modbus_t *ctx, uint8_t *req,

/* Exception code */
if (function >= 0x80) {
if (rsp_length == (offset + 2 + (int)ctx->backend->checksum_length) &&
if (rsp_length == (int)(offset + 2 + ctx->backend->checksum_length) &&
req[offset] == (rsp[offset] - 0x80)) {
/* Valid exception code received */

Expand Down Expand Up @@ -705,7 +705,7 @@ static int response_exception(modbus_t *ctx, sft_t *sft,
int modbus_reply(modbus_t *ctx, const uint8_t *req,
int req_length, modbus_mapping_t *mb_mapping)
{
int offset;
unsigned int offset;
int slave;
int function;
uint16_t address;
Expand Down Expand Up @@ -1003,7 +1003,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
int modbus_reply_exception(modbus_t *ctx, const uint8_t *req,
unsigned int exception_code)
{
int offset;
unsigned int offset;
int slave;
int function;
uint8_t rsp[MAX_MESSAGE_LENGTH];
Expand Down Expand Up @@ -1049,10 +1049,11 @@ static int read_io_status(modbus_t *ctx, int function,

rc = send_msg(ctx, req, req_length);
if (rc > 0) {
int i, temp, bit;
unsigned int i;
int temp, bit;
int pos = 0;
int offset;
int offset_end;
unsigned int offset;
unsigned int offset_end;

rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
if (rc == -1)
Expand Down Expand Up @@ -1160,7 +1161,7 @@ static int read_registers(modbus_t *ctx, int function, int addr, int nb,

rc = send_msg(ctx, req, req_length);
if (rc > 0) {
int offset;
unsigned int offset;
int i;

rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
Expand Down Expand Up @@ -1493,7 +1494,7 @@ int modbus_write_and_read_registers(modbus_t *ctx,

rc = send_msg(ctx, req, req_length);
if (rc > 0) {
int offset;
unsigned int offset;

rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
if (rc == -1)
Expand Down Expand Up @@ -1536,7 +1537,7 @@ int modbus_report_slave_id(modbus_t *ctx, int max_dest, uint8_t *dest)
rc = send_msg(ctx, req, req_length);
if (rc > 0) {
int i;
int offset;
unsigned int offset;
uint8_t rsp[MAX_MESSAGE_LENGTH];

rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
Expand Down Expand Up @@ -1711,7 +1712,7 @@ int modbus_set_indication_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_us
return 0;
}

int modbus_get_header_length(modbus_t *ctx)
unsigned int modbus_get_header_length(modbus_t *ctx)
{
if (ctx == NULL) {
errno = EINVAL;
Expand Down
2 changes: 1 addition & 1 deletion src/modbus.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ MODBUS_API int modbus_set_byte_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t
MODBUS_API int modbus_get_indication_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec);
MODBUS_API int modbus_set_indication_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec);

MODBUS_API int modbus_get_header_length(modbus_t *ctx);
MODBUS_API unsigned int modbus_get_header_length(modbus_t *ctx);

MODBUS_API int modbus_connect(modbus_t *ctx);
MODBUS_API void modbus_close(modbus_t *ctx);
Expand Down
4 changes: 4 additions & 0 deletions tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ noinst_PROGRAMS = \
random-test-client \
unit-test-server \
unit-test-client \
fuzzing_harness \
version

common_ldflags = \
Expand All @@ -34,6 +35,9 @@ unit_test_server_LDADD = $(common_ldflags)
unit_test_client_SOURCES = unit-test-client.c unit-test.h
unit_test_client_LDADD = $(common_ldflags)

fuzzing_harness_SOURCES = fuzzing_harness.c unit-test.h
fuzzing_harness_LDADD = $(common_ldflags)

version_SOURCES = version.c
version_LDADD = $(common_ldflags)

Expand Down
51 changes: 51 additions & 0 deletions tests/README.fuzzing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Compilation
```bash
CC="<AFL_ROOT>/afl-gcc" CXX="<AFL_ROOT>/afl-g++" ./configure --disable-shared
make
```

# Generating Seeds
## Generate
A simple way to get started is to apply this or similar patches and run the unit tests.
```patch
diff --git a/src/modbus-tcp.c b/src/modbus-tcp.c
index a0846f2..1f1b5df 100644
--- a/src/modbus-tcp.c
+++ b/src/modbus-tcp.c
@@ -163,12 +163,20 @@ static int _modbus_tcp_send_msg_pre(uint8_t *req, int req_length)
return req_length;
}

+int fc = 0;
+
static ssize_t _modbus_tcp_send(modbus_t *ctx, const uint8_t *req, int req_length)
{
/* MSG_NOSIGNAL
Requests not to send SIGPIPE on errors on stream oriented
sockets when the other end breaks the connection. The EPIPE
error is still returned. */
+ char *filename = (char*)malloc(1024 * sizeof(char));
+ sprintf(filename, "fuzzing-seeds/test-case-%d.tcp.raw", fc++);
+ FILE* f = fopen(filename, "w");
+ fwrite(req, sizeof(uint8_t), req_length, f);
+ fclose(f);
+ free(filename);
return send(ctx->s, (const char *)req, req_length, MSG_NOSIGNAL);
}
```

## Minimise
AFL has a tool to minimise the number of seeds by removing duplicate or "uninteresting" seeds. This improves the later fuzzing startup times.
```bash
<AFL_ROOT>/afl-cmin -i fuzzing-seeds -o fuzzing-seeds-cmin tests/fuzzing_harness
```

# Fuzzing
```bash
<AFL_ROOT>/afl-fuzz -i fuzzing-seeds-cmin -o fuzzing-results -M fuzzerMaster -- tests/fuzzing_harness
<AFL_ROOT>/afl-fuzz -i fuzzing-seeds-cmin -o fuzzing-results -S fuzzerSlaveX -- tests/fuzzing_harness
```

# Notes
* AFL does not like to be on the PATH during configure/make
* Try with ```afl-clang-fast``` and ```afl-clang-fast++``` compilers for speed up
125 changes: 125 additions & 0 deletions tests/fuzzing_harness.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* Copyright © 2008-2014 Stéphane Raimbault <[email protected]>
*
* SPDX-License-Identifier: BSD-3-Clause
*/

#include <stdio.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <error.h>
//#include <fcntl.h>

#include <modbus.h>

#define MAX_FUZZ_INPUT_SIZE 5120

int main(int argc, char * argv [])
{
// general
int e;

// server
int socket_of_server = -1;
modbus_t *ctx;
modbus_mapping_t *mb_mapping = NULL;

// fuzz and client
size_t insize;
char buf[MAX_FUZZ_INPUT_SIZE];
int socket_to_server;
struct sockaddr_in serv_addr;
socklen_t sizeof_serv_addr = sizeof(serv_addr);

// init server
ctx = modbus_new_tcp("127.0.0.1", 0);
socket_of_server = modbus_tcp_listen(ctx, 1);
if (socket_of_server < 0) {
error(-1, 1, "Failed to start server: %s", modbus_strerror(errno));
}

// get the port of the server
int getsockname_result = getsockname(socket_of_server, &serv_addr, &sizeof_serv_addr);
if ( getsockname_result < 0 ) {
error(-1, getsockname_result, "Failed to get server port number");
}

#ifdef __AFL_LOOP
while (__AFL_LOOP(1000)) {
#else
do {
#endif
uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
int rc;

// Reset state from last fuzz
modbus_mapping_free(mb_mapping);
mb_mapping = modbus_mapping_new(500, 500, 500, 500);
if (mb_mapping == NULL) {
error(-1, -1, "Failed to allocate the mapping: %s", modbus_strerror(errno));
}

// Read fizzing data to send to server
memset(buf, 0, sizeof(buf));
insize = fread(buf, 1, sizeof(buf), stdin);
// TODO: Confirm whole test case was read

// Connect to server
socket_to_server = socket(AF_INET, SOCK_STREAM, 0);
if (socket_to_server < 0) {
error(1, socket_to_server, "ERROR opening socket");
}
e = connect(socket_to_server, (struct sockaddr *) &serv_addr, sizeof_serv_addr);
if(e < 0) {
error(1, e, "\nError : Connect to server Failed\n");
}

// Send fuzzing data to server
sendto(socket_to_server, buf, insize, 0, NULL, -1);

// Run a general ModBus server logic
modbus_tcp_accept(ctx, &socket_of_server);
rc = modbus_receive(ctx, query);
if (rc > 0) {
/* rc is the query size */
modbus_reply(ctx, query, rc, mb_mapping);
} else if (rc == -1) {
/* Connection closed by the client or error */
break;
}

// Read response from server and close connection
e = read(socket_to_server, buf, sizeof(buf));
if (e < 0) {
error(1, e, "Failed to read response from server");
}
e = close(socket_to_server);
if (e < 0) {
error(1, e, "Failed to close socket to server");
}

#ifdef __AFL_LOOP
}
#else
} while (0);
#endif

if (socket_of_server >= 0) {
close(socket_of_server);
}
if (socket_to_server >= 0) {
close(socket_to_server);
}
modbus_mapping_free(mb_mapping);
modbus_close(ctx);
modbus_free(ctx);

return 0;
}