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

Fuzzing: add fuzz testing #21235

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Makefile.in
/compile_commands.json
/config.*
/configure
/configure~
/dist/
/frob-websocket
/lcov/
Expand All @@ -59,6 +60,7 @@ Makefile.in
/tags
/test-*
/test_rsa_key
/fuzz_*
/tmp-dist
/tmp/
/tsconfig.tsbuildinfo
Expand All @@ -81,6 +83,7 @@ Makefile.in
/src/tls/cockpit-certificate-helper
/src/ws/cockpit-desktop
/src/ws/cockpit.appdata.xml
/src/ws/*.appdata.xml
/src/ws/mock-dbus-tests.[ch]
/test/images/
/tools/compile
Expand Down
40 changes: 40 additions & 0 deletions FUZZING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Fuzzing cockpit

## Build cockpit fuzzer using libFuzzer.

### Export flags for fuzzing.

Note that in `CFLAGS` and `CXXFLAGS`, any type of sanitizers can be added.

- [AddressSanitizer](https://clang.llvm.org/docs/AddressSanitizer.html),
[ThreadSanitizer](https://clang.llvm.org/docs/ThreadSanitizer.html),
[MemorySanitizer](https://clang.llvm.org/docs/MemorySanitizer.html),
[UndefinedBehaviorSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html),
[LeakSanitizer](https://clang.llvm.org/docs/LeakSanitizer.html).

```shell
$ export CC=clang
$ export CXX=clang++
$ export CFLAGS="-g -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=address,undefined -fsanitize=fuzzer-no-link"
$ export CXXFLAGS="-g -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=address,undefined -fsanitize=fuzzer-no-link"
$ export LIB_FUZZING_ENGINE="-fsanitize=fuzzer"
```

### Build cockpit for fuzzing.

```shell
$ ./autogen.sh --enable-fuzzing --disable-doc
$ make -j$(nproc)
```

### Running fuzzer.

```shell
$ mkdir -p fuzz_authorize_seed fuzz_base64_seed fuzz_websocket_seed

$ ./fuzz_authorize fuzz_authorize_seed src/common/fuzz_authorize_seed_corpus
$ ./fuzz_base64 fuzz_base64_seed src/common/fuzz_base64_seed_corpus
$ ./fuzz_websocket fuzz_websocket_seed src/websocket/fuzz_websocket_seed_corpus
```

Here is more information about [LibFuzzer](https://llvm.org/docs/LibFuzzer.html).
22 changes: 22 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,27 @@ AC_DEFINE_UNQUOTED([PATH_SSH_ADD], ["$SSH_ADD"], [Location of ssh-add binary])
AC_PATH_PROG([SSH_AGENT], [ssh-agent], [/usr/bin/ssh-agent], [$PATH:/usr/local/bin:/usr/bin:/bin])
AC_DEFINE_UNQUOTED([PATH_SSH_AGENT], ["$SSH_AGENT"], [Location of ssh-agent binary])

# Fuzzing
AC_MSG_CHECKING([for fuzzing])
AC_ARG_ENABLE(fuzzing,
AS_HELP_STRING([--enable-fuzzing=no/yes],
[Turn the fuzzing on or off])
)

if test "$enable_fuzzing" = "yes"; then
if test -z "$LIB_FUZZING_ENGINE"; then
FUZZING_ENGINE="-fsanitize=fuzzer"
else
FUZZING_ENGINE="$LIB_FUZZING_ENGINE"
fi
AC_SUBST(FUZZING_ENGINE)
fuzzing_status="yes"
else
fuzzing_status="no"
fi
AM_CONDITIONAL(WITH_FUZZING, test "$enable_fuzzing" = "yes")
AC_MSG_RESULT($fuzzing_status)

# Address sanitizer
AC_MSG_CHECKING([for asan flags])
AC_ARG_ENABLE(asan,
Expand Down Expand Up @@ -371,6 +392,7 @@ echo "
Debug mode: ${debug_status}
Node environment: ${NODE_ENV}
With coverage: ${enable_coverage}
With fuzzing: ${fuzzing_status}
With address sanitizer: ${asan_status}
SELinux Policy: ${enable_selinux_policy}

Expand Down
12 changes: 12 additions & 0 deletions src/common/Makefile-common.am
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,15 @@ TEST_PROGRAM += test-webserver
test_webserver_CPPFLAGS = $(libcockpit_common_a_CPPFLAGS) $(TEST_CPP)
test_webserver_LDADD = $(TEST_LIBS)
test_webserver_SOURCES = src/common/test-webserver.c

if WITH_FUZZING
noinst_PROGRAMS += fuzz_authorize
fuzz_authorize_SOURCES = src/common/fuzz_authorize.c
fuzz_authorize_CPPFLAGS = $(libcockpit_common_a_CPPFLAGS)
fuzz_authorize_LDADD = $(libcockpit_common_a_LIBS) $(FUZZING_ENGINE)

noinst_PROGRAMS += fuzz_base64
fuzz_base64_SOURCES = src/common/fuzz_base64.c
fuzz_base64_CPPFLAGS = $(libcockpit_common_a_CPPFLAGS)
fuzz_base64_LDADD = $(libcockpit_common_a_LIBS) $(FUZZING_ENGINE)
endif
59 changes: 59 additions & 0 deletions src/common/fuzz_authorize.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#include "config.h"

#include "cockpitauthorize.h"

#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#define kMinInputLength 2
#define kMaxInputLength 1024

extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);

int
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
char *data_in;

if (size < kMinInputLength || size > kMaxInputLength)
return 0;

data_in = calloc(size + 1, sizeof(char));
if (data_in == NULL)
return 0;

memcpy(data_in, data, size);

{
char *user = "a";
char *password = NULL;

password = cockpit_authorize_parse_basic(data_in, &user);
if (password != NULL) {
free(password);
free(user);
}
}
{
void *result = NULL;

result = cockpit_authorize_parse_negotiate(data_in, NULL);
if (result != NULL)
free(result);
}
{
void *result = NULL;
char *conversation = NULL;

result = cockpit_authorize_parse_x_conversation(data_in, &conversation);
if (result != NULL)
free(result);

if (conversation != NULL)
free(conversation);
}

free(data_in);
return 0;
}
1 change: 1 addition & 0 deletions src/common/fuzz_authorize_seed_corpus/basic_fixtures.bin
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Basic c2NydWZmeTp6ZXJvZw==
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Negotiate c2NydWZmeTp6ZXJvZw==
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
X-Conversation abcdefghi c2NydWZmeTp6ZXJvZw==
25 changes: 25 additions & 0 deletions src/common/fuzz_base64.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include "config.h"

#include "cockpitbase64.h"

#include <stdint.h>

#define kMinInputLength 2
#define kMaxInputLength 1024

extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);

int
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
char encoded[2048];
uint8_t decoded[2048];

if (size < kMinInputLength || size > kMaxInputLength)
return 0;

cockpit_base64_ntop(data, size, encoded, sizeof(encoded));
cockpit_base64_pton((char *)data, size, decoded, sizeof(decoded));

return 0;
}
1 change: 1 addition & 0 deletions src/common/fuzz_base64_seed_corpus/decoded.bin
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
QUFBQQ==
1 change: 1 addition & 0 deletions src/common/fuzz_base64_seed_corpus/encoded.bin
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
AAAA
7 changes: 7 additions & 0 deletions src/websocket/Makefile-websocket.am
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,10 @@ TEST_PROGRAM += test-websocket
test_websocket_CPPFLAGS = $(libwebsocket_a_CPPFLAGS) $(TEST_CPP)
test_websocket_LDADD = $(libwebsocket_a_LIBS) $(TEST_LIBS)
test_websocket_SOURCES = src/websocket/test-websocket.c

if WITH_FUZZING
noinst_PROGRAMS += fuzz_websocket
fuzz_websocket_SOURCES = src/websocket/fuzz_websocket.c
fuzz_websocket_CPPFLAGS = $(libwebsocket_a_CPPFLAGS)
fuzz_websocket_LDADD = $(libwebsocket_a_LIBS) $(libcockpit_common_a_LIBS) $(FUZZING_ENGINE)
endif
56 changes: 56 additions & 0 deletions src/websocket/fuzz_websocket.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#include "config.h"

#include "websocket.h"
#include "websocketprivate.h"

#include <stdint.h>

#define kMinInputLength 2
#define kMaxInputLength 1024

extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);

int
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
char *data_in;

if (size < kMinInputLength || size > kMaxInputLength)
return 0;

data_in = calloc(size + 1, sizeof(char));
if (data_in == NULL)
return 0;

memcpy(data_in, data, size);

{
gchar *path = NULL;
gchar *method = NULL;

web_socket_util_parse_req_line((char *)data, size, &method, &path);
if (method != NULL)
g_free(method);

if (path != NULL)
g_free(path);
}
{
guint status;
gchar *reason = NULL;

web_socket_util_parse_status_line(data_in, size + 1, NULL, &status, &reason);
if (reason != NULL)
g_free(reason);
}
{
GHashTable *headers = NULL;

web_socket_util_parse_headers(data_in, size + 1, &headers);
if (headers != NULL)
g_hash_table_unref(headers);
}

free(data_in);
return 0;
}
1 change: 1 addition & 0 deletions src/websocket/fuzz_websocket_seed_corpus/headers.bin
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Host:https://cockpit-project.org
2 changes: 2 additions & 0 deletions src/websocket/fuzz_websocket_seed_corpus/req_line.bin
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
GET /path/part HTTP/1.0

2 changes: 2 additions & 0 deletions src/websocket/fuzz_websocket_seed_corpus/status_line.bin
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
HTTP/1.0 101 Switching Protocols